You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

问题原因

  1. 工作表索引越界:EPPlus中Worksheets集合的索引是从0开始的,原代码中package.Workbook.Worksheets[1]会尝试访问第二个工作表,如果Excel文件只有一个工作表,就会触发索引越界异常。
  2. Excel读取逻辑错误:遍历单元格的方式会重复创建数据行(每行的每个单元格都会触发一次行创建),导致数据重复且效率低下。
  3. 表名安全问题:直接用文件名作为表名,若文件名包含特殊字符(空格、中文、[]等),会导致SQL语句执行失败。
  4. 未处理空工作表情况:未检查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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:35:55