最近做了一个.NET Framework 程序,将从远程接口请求的数据同步到sqlserver数据库中;当数据量小的时候,常规的添加操作是没什么问题的。但数据量比较大时(通常1000行及以上),会花费比较多的时间。这时SqlBulkCopy就能显著的解决这个问题。

1、SqlBulkCopy介绍

SqlBulkCopy是.NET中的一个包,用于将大批量数据添加到sqlserver数据库中,相比逐行插入它的速度更快,并且支持分批处理,失败事务回滚、设置延迟等。

2、SqlBulkCopy使用

这里封装了一个公共方法,用于将指定的数据插入到表中。

/// <summary>
/// 使用SqlBulkCopy批量添加数据
/// </summary>
/// <param name="connStr">数据库连接字符串</param>
/// <param name="tableName">表名</param>
/// <param name="datas">数据</param>
/// <param name="batchSize">大小</param>
/// <param name="timeout">延迟</param>
public void BulkInsert<T>(string connStr, string tableName, IEnumerable<T> datas, int batchSize, int timeout)
{
    var dataList = new List<T>(datas);
    while (dataList.Count > 0)
    {
        // 计算当前批次的实际大小
        int currentBatchSize = Math.Min(batchSize, dataList.Count);
        // 获取当前批次的数据
        List<T> batchData = dataList.GetRange(0, currentBatchSize);
        using (var conn = new SqlConnection(connStr))
        {
            // 打开连接
            conn.Open();
            // 开始事务
            SqlTransaction tran = conn.BeginTransaction();
            try
            {
                // 创建 SqlBulkCopy 实例,并进行设置
                using (var bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, tran))
                {
                    bulkCopy.DestinationTableName = tableName;
                    bulkCopy.BatchSize = batchSize;
                    bulkCopy.BulkCopyTimeout = timeout;
                    // 启用流式传输
                    bulkCopy.EnableStreaming = true;
                    var properties = typeof(T).GetProperties().OrderBy(p => p.MetadataToken).ToArray();
                    foreach (var prop in properties)
                    {
                        bulkCopy.ColumnMappings.Add(prop.Name, prop.Name);
                    }
                    // 使用ObjectReader流式读取数据
                    using (var reader = ObjectReader.Create(batchData))
                    {
                        bulkCopy.WriteToServer(reader);
                    }
                }
                // 提交事务
                tran.Commit();
                // 成功提交后移除当前批次的数据
                dataList.RemoveRange(0, currentBatchSize);
            }
            catch
            {
                // 发生异常时回滚事务
                tran.Rollback();
                throw;
            }
        }
    }
}

注意:在Modal中申明的实体需要和表中的顺序一一对应
举个例子:
Modal中定义User:

public class User
{
    public Guid UserId { get; set; }
    public string Username { get; set; }
    public string Hobby { get; set; }
}

实际User表结构:
User表
食用方式:

var users = new List<User>(){
	new User(){UserId = Guid.NewGuid(), Username = "小一", Hobby = "旅游"},
	new User(){UserId = Guid.NewGuid(), Username = "小二", Hobby = "散步"}
}
string connStr = GetConnectionString(); // 自行定义
BulkInsert(connStr, "dbo.user", users, 1000, 30);