WPF应用Excel上传SQL Server失败:工作表索引越界求助
Excel文件上传SQL Server失败排查与解决
问题描述
开发了带GUI的WPF应用,支持上传Excel文件,预期将每个文件作为独立数据表存入SQL Server,表列与Excel列完全匹配,但文件始终无法存入数据库。错误提示工作表无数据或找不到工作表,已确认上传文件包含有效数据和工作表。
功能模块代码
文件选择(功能正常)
private void Button_Click(object sender, RoutedEventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog() { Multiselect = true }; bool? response = openFileDialog.ShowDialog(); if (response == true) { // 获取选中的文件 string[] files = openFileDialog.FileNames; UploadFiles(files); } }
文件上传(无法存入SQL Server)
private void UploadFiles(string[] files) { // 遍历并上传所有选中的文件 foreach (string filePath in files) { string filename = System.IO.Path.GetFileName(filePath); FileInfo fileInfo = new FileInfo(filePath); // 读取Excel数据到DataTable DataTable excelData = ReadExcelFile(filePath); if (excelData != null) { // 从文件名获取表名 string tableName = Path.GetFileNameWithoutExtension(filePath); // 在SQL Server中创建表 CreateTable(tableName, excelData); UploadingFilesList.Items.Add(new fileDetail() { FileName = filename, // 字节转MB FileSize = string.Format("{0} {1}", (fileInfo.Length / 1.049e+6).ToString("0.0"), "Mb"), UploadProgress = 100 }); MessageBox.Show("文件已上传并存储到数据库。"); } else { MessageBox.Show("无法读取Excel文件。"); } } }
读取Excel文件(无法识别数据或工作表)
private DataTable ReadExcelFile(string filePath) { try { using (var package = new ExcelPackage(new FileInfo(filePath))) { ExcelWorksheet worksheet = package.Workbook.Worksheets[1]; ExcelRangeBase range = worksheet.Cells[worksheet.Dimension.Address]; DataTable dataTable = new DataTable(); foreach (var cell in range) { if (cell.Start.Row == 1) { dataTable.Columns.Add(cell.Text); } else { DataRow dataRow = dataTable.NewRow(); for (int col = 1; col <= range.Columns; col++) { dataRow[col - 1] = worksheet.Cells[cell.Start.Row, col].Value; } dataTable.Rows.Add(dataRow); } } return dataTable; } } catch (Exception ex) { MessageBox.Show("读取Excel文件出错: " + ex.Message); return null; } }
创建数据表(无法创建表)
private void CreateTable(string tableName, DataTable tableData) { string connectionString = "Connection"; // 代码中已替换为真实连接字符串 try { using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); // 构建CREATE TABLE语句 string createTableQuery = "CREATE TABLE " + tableName + " ("; foreach (DataColumn column in tableData.Columns) { createTableQuery += "[" + column.ColumnName + "] VARCHAR(MAX), "; } createTableQuery = createTableQuery.TrimEnd(',', ' ') + ")"; using (SqlCommand command = new SqlCommand(createTableQuery, connection)) { command.ExecuteNonQuery(); } // 批量插入数据到新表 using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = tableName; bulkCopy.WriteToServer(tableData); } connection.Close(); } } catch (Exception ex) { MessageBox.Show("创建表或存储数据出错: " + ex.Message); } }
诊断信息
添加诊断代码后,输出错误:
Exception thrown: 'System.IndexOutOfRangeException' in EPPlus.dll
Error occurred while reading Excel file: Worksheet position out of range.
问题原因
- 工作表索引越界:EPPlus中
Worksheets集合的索引是从0开始的,原代码中package.Workbook.Worksheets[1]会尝试访问第二个工作表,如果Excel文件只有一个工作表,就会触发索引越界异常。 - Excel读取逻辑错误:遍历单元格的方式会重复创建数据行(每行的每个单元格都会触发一次行创建),导致数据重复且效率低下。
- 表名安全问题:直接用文件名作为表名,若文件名包含特殊字符(空格、中文、
[]等),会导致SQL语句执行失败。 - 未处理空工作表情况:未检查
worksheet.Dimension是否为null,若工作表确实无数据(虽然用户确认有数据,但需做防御性处理)会引发异常。
修复方案
1. 修复工作表索引问题
改为获取第一个工作表,并增加空判断:
private DataTable ReadExcelFile(string filePath) { try { using (var package = new ExcelPackage(new FileInfo(filePath))) { // 获取第一个工作表(索引从0开始) ExcelWorksheet worksheet = package.Workbook.Worksheets.FirstOrDefault(); if (worksheet == null) { MessageBox.Show("文件中未找到有效工作表"); return null; } var dimension = worksheet.Dimension; if (dimension == null) { MessageBox.Show("工作表中无有效数据"); return null; } // 后续读取逻辑... } } // 异常处理... }
2. 重构Excel数据读取逻辑
改为按行读取,避免重复创建行:
private DataTable ReadExcelFile(string filePath) { try { using (var package = new ExcelPackage(new FileInfo(filePath))) { ExcelWorksheet worksheet = package.Workbook.Worksheets.FirstOrDefault(); if (worksheet == null) { MessageBox.Show("文件中未找到有效工作表"); return null; } var dimension = worksheet.Dimension; if (dimension == null) { MessageBox.Show("工作表中无有效数据"); return null; } DataTable dataTable = new DataTable(); // 读取表头(第一行) for (int col = dimension.Start.Column; col <= dimension.End.Column; col++) { string headerText = worksheet.Cells[dimension.Start.Row, col].Text; // 处理空表头,自动生成列名 dataTable.Columns.Add(string.IsNullOrEmpty(headerText) ? $"Column_{col}" : headerText); } // 读取数据行(从第二行开始) for (int row = dimension.Start.Row + 1; row <= dimension.End.Row; row++) { DataRow dataRow = dataTable.NewRow(); for (int col = dimension.Start.Column; col <= dimension.End.Column; col++) { dataRow[col - dimension.Start.Column] = worksheet.Cells[row, col].Value; } dataTable.Rows.Add(dataRow); } return dataTable; } } catch (Exception ex) { Debug.WriteLine("读取Excel文件出错: " + ex.Message); MessageBox.Show("读取Excel文件出错: " + ex.Message); return null; } }
3. 修复数据表创建的安全与稳定性问题
处理表名特殊字符,优化连接释放逻辑:
private void CreateTable(string tableName, DataTable tableData) { string connectionString = "你的真实连接字符串"; // 处理表名:转义方括号,避免SQL语法错误 string safeTableName = $"[{tableName.Replace("]", "]]")}]"; try { using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); // 用StringBuilder构建SQL语句,避免拼接错误 StringBuilder createTableQuery = new StringBuilder($"CREATE TABLE {safeTableName} ("); foreach (DataColumn column in tableData.Columns) { string safeColumnName = $"[{column.ColumnName.Replace("]", "]]")}]"; createTableQuery.Append($"{safeColumnName} VARCHAR(MAX), "); } // 移除最后一个多余的逗号和空格 createTableQuery.Remove(createTableQuery.Length - 2, 2); createTableQuery.Append(")"); using (SqlCommand command = new SqlCommand(createTableQuery.ToString(), connection)) { command.ExecuteNonQuery(); } // 批量插入时添加列映射,确保列名匹配 using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = safeTableName; foreach (DataColumn col in tableData.Columns) { bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName); } bulkCopy.WriteToServer(tableData); } // using块会自动释放连接,无需手动Close } } catch (Exception ex) { Debug.WriteLine("创建表或存储数据出错: " + ex.Message); MessageBox.Show("创建表或存储数据出错: " + ex.Message); } }
4. 其他优化建议
- 文件格式验证:在文件选择对话框中添加过滤器,限制仅能选择
.xlsx格式(EPPlus对.xls支持有限):OpenFileDialog openFileDialog = new OpenFileDialog() { Multiselect = true, Filter = "Excel文件 (*.xlsx)|*.xlsx" }; - 重复表名处理:创建表前检查数据库中是否已存在同名表,可提示用户或自动添加后缀(如
_1)避免冲突。 - 批量上传反馈:多个文件上传时,替换弹窗提示为进度条更新,提升用户体验。
内容的提问来源于stack exchange,提问作者appdevlaur
相关产品推荐
相关产品推荐

