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

SqlBulkCopy.WriteToServerAsync无法写入SQL Server表,同步方法正常

问题原因分析
  • 异步操作未等待:直接调用WriteToServerAsync或用Task.Run包裹但不等待,导致代码提前执行dataTable.Clear()或_connection.Close(),此时异步写入任务尚未完成,数据被清空或连接被中断,最终无数据写入数据库。
  • 同步流程与异步操作不兼容:原代码采用同步循环读取逻辑,异步写入未与读取流程协同,导致资源提前释放。
修复方案:正确实现异步写入

1. 改造方法为异步模式

将整个导入方法声明为async Task,全程使用异步IO操作,确保所有异步任务执行完成后再进行后续步骤。

2. 修正后的核心代码

public async Task ImportCsvAsync(string FilePath, string TargetTable, int BatchSize, int columnsInCsv)
{
    var dataTable = new DataTable(TargetTable);
    using var streamReader = new StreamReader(FilePath);
    using var bulkCopy = new SqlBulkCopy(this._connection, SqlBulkCopyOptions.TableLock, null)
    {
        DestinationTableName = TargetTable,
        BulkCopyTimeout = 0,
        BatchSize = BatchSize,
    };

    // 此处需提前完成DataTable列与SQL表的映射配置
    // ...

    var outputFields = new string[columnsInCsv];
    await this._connection.OpenAsync();
    string line;
    int totalRows = 0;
    int rowCounter = 0;

    while ((line = await streamReader.ReadLineAsync()) != null)
    {
        CsvTools.ParseCsvLineWriteDirect(line, ref outputFields);
        dataTable.LoadDataRow(outputFields, true);

        totalRows++;
        rowCounter++;

        if (rowCounter >= BatchSize)
        {
            try
            {
                await bulkCopy.WriteToServerAsync(dataTable);
                rowCounter = 0;
                dataTable.Clear();
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.ToString());
                return;
            }
        }
    }

    if (dataTable.Rows.Count > 0)
    {
        await bulkCopy.WriteToServerAsync(dataTable);
        dataTable.Clear();
    }

    await this._connection.CloseAsync();
}

3. 关键修改说明

  • 用await streamReader.ReadLineAsync()替代同步读取,避免阻塞线程,提升文件IO利用率。
  • 调用await bulkCopy.WriteToServerAsync(dataTable)确保数据库写入完成后,再执行清空DataTable或关闭连接的操作。
  • 配套使用OpenAsync和CloseAsync的异步版本,保持全流程异步一致性。
进一步性能优化建议
  • 并行解析与写入:使用Channel<T>实现生产者-消费者模式,一个线程异步解析CSV行并写入Channel,另一个线程从Channel读取数据批量写入数据库,重叠文件IO与数据库IO操作,大幅提升整体导入速度。
  • 调整批次大小:根据服务器内存和数据库性能测试最优BatchSize(例如10000、50000),过大批次会占用过多内存,过小则增加数据库交互次数。
  • 禁用非必要约束:若数据合法性已提前验证,可移除SqlBulkCopyOptions.CheckConstraints、SqlBulkCopyOptions.FireTriggers等选项,减少数据库端校验开销。
  • 预配置DataTable列:确保DataTable列类型与目标SQL表完全匹配,避免自动类型转换带来的性能损耗。

内容的提问来源于stack exchange,提问作者LSM07

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:21:07