使用 SqlBulkCopy 进行大批量数据库插入

当涉及新老系统迁移的时候,可能同一个模块的数据表结构变化十分大,则需要通过编写一些程序来进行一些转换操作,转换完之后尝尝少则几万条数据,多则几十万条,如果一条一条的插入所消耗的时间成本是十分昂贵的。

那么在C#当中则提供了SqlBulkCopy类,它存在于System.Data.SqlClient命名空间下,该类可以进行大批量的数据插入,并且效率十分快。
经过测试,耗时时间如下:

  • 十万条数据:2.2051s
  • 一万条数据:0.2188s
  • 一千条数据:0.0187s

效率十分的高,但是该类仅支持DataTable操作,一般来说Dapper以及EF框架读出来的都是一个IEnumerable,所以我们需要通过反射来构建一个DataTable进行操作。这里我写了一个静态泛型方法,可以直接传入IEnumerbale对象来批量插入数据:

static void BulkInsertAll<T>(IEnumerable<T> entities,String connectString)
{
    var conn = new SqlConnection(connectString);
    conn.Open();

    Type t = typeof(T);
    var tableAttribute = (TableAttribute)t.GetCustomAttributes(typeof(TableAttribute), false).Single();
    var bulkCopy = new SqlBulkCopy(conn) { DestinationTableName = tableAttribute.Name };
    var properties = t.GetMembers().Where(p =>
    {
	var columnAttribute = Attribute.GetCustomAttribute(p, typeof(ColumnAttribute)) as ColumnAttribute;
	if (columnAttribute != null) return true;
	return false;
	}).ToArray();
	var table = new DataTable();
	    foreach (var property in properties)
	    {
		Type propertyType = ((FieldInfo)property).FieldType;
		if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
		    {
			propertyType = Nullable.GetUnderlyingType(propertyType);
		    }
		    table.Columns.Add(new DataColumn(property.Name, propertyType));
	    }
	    foreach (var entity in entities)
	    {
		var cols = properties.Select(property =>
		{
			var field = (FieldInfo)property;
			var o = field.GetValue(entity);
			if (o == null)
				return DBNull.Value;
			else
				return o;
			}).ToArray();
			table.Rows.Add(cols);
		}
	    bulkCopy.WriteToServer(table);
	    conn.Close();
     }
}