使用LINQ to SQL进行批量插入

18

我有一个查询看起来像这样:

using (MyDC TheDC = new MyDC())
{
   foreach (MyObject TheObject in TheListOfMyObjects)
   {
      DBTable TheTable = new DBTable();

      TheTable.Prop1 = TheObject.Prop1;
      .....
      TheDC.DBTables.InsertOnSubmit(TheTable);

   }
   TheDC.SubmitChanges();
}

这个查询基本上使用 Linq-to-SQL 将列表插入数据库。现在我在网上读到 L2S 不支持批量操作。

我的查询是逐个插入每个元素,还是一次性写入所有元素?

感谢澄清。

5个回答

34
我修改了以下链接中的代码,使其更加高效,并在我的应用程序中使用它。这很方便,因为您可以将其放在当前自动生成的类的顶部的一个部分类中。不需要使用InsertOnSubmit将实体添加到列表中,而是调用YourDataContext.BulkInsertAll(list)代替SubmitChanges

http://www.codeproject.com/Tips/297582/Using-bulk-insert-with-your-linq-to-sql-datacontex

partial void OnCreated()
{
    CommandTimeout = 5 * 60;
}

public void BulkInsertAll<T>(IEnumerable<T> entities)
{                        
    using( var conn = new SqlConnection(Connection.ConnectionString))
    {
        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.GetProperties().Where(EventTypeFilter).ToArray();
        var table = new DataTable();

        foreach (var property in properties)
        {
            Type propertyType = property.PropertyType;
            if (propertyType.IsGenericType &&
                propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                propertyType = Nullable.GetUnderlyingType(propertyType);
            }

            table.Columns.Add(new DataColumn(property.Name, propertyType));
        }

        foreach (var entity in entities)
        {
            table.Rows.Add(
                properties.Select(
                property => property.GetValue(entity, null) ?? DBNull.Value
                ).ToArray());
        }

        bulkCopy.WriteToServer(table);
    }                                               
}

private bool EventTypeFilter(System.Reflection.PropertyInfo p)
{
    var attribute = Attribute.GetCustomAttribute(p,
        typeof(AssociationAttribute)) as AssociationAttribute;

    if (attribute == null) return true;
    if (attribute.IsForeignKey == false) return true;

    return false;
}

2
使用Linqpad,我必须将t.GetProperties().Where(EventTypeFilter)更改为t.GetFields()。仅用了一分钟多一点就插入了20万行数据!(y) - Kevin Candlert
看看这个:https://dev59.com/rXvaa4cB1Zd3GeqPHLwb#21382542 - 对我很有效。 - avs099
如何在不需要新的数据上下文的情况下获取和设置批量插入记录的ID属性? - Dan_J
1
我把EF的SaveChanges()留过夜,早上发现我的虚拟机已经挂了。这个操作使得100万条记录在8秒内插入,非常感谢! - DylanB
我在处理具有关联(EntitySet)属性的类时遇到了问题。然后该方法会抛出columnMapping异常。我该如何处理? - gegy
显示剩余2条评论

8
术语“Bulk Insert”通常指的是SQL Server特定的基于bcp的SqlBulkCopy实现,它构建在IRowsetFastLoad之上,速度非常快。
Linq-2-SQL不会在任何情况下使用此机制进行插入操作。
如果您需要将数据批量加载到SQL Server并且需要快速,请建议手动编写SqlBulkCopy。
Linq-2-SQL将尝试执行一些优化以加速多个插入操作,但仍无法达到许多微型ORM的水平(即使我所知道的没有一个微型ORM实现了SqlBulkCopy)。

4
它将为每个记录生成单个插入语句,但会将它们全部发送到服务器作为单个批次并在单个事务中运行。 这就是循环外的SubmitChanges()所做的。 如果你把它放在循环里面,那么每次迭代都会为INSERT去服务器,并在它自己的事务中运行。 我不认为有任何方法可以触发SQL BULK INSERT。

我已经有一段时间没有测试L2S插入性能与Dapper的对比了,但我确实测试了EF与Dapper的性能……结果令人失望。这是测试代码:https://gist.github.com/1623514 - Sam Saffron

1
LINQ单个插入来自列表:
                int i = 0;
                foreach (IPAPM_SRVC_NTTN_NODE_MAP item in ipapmList)
                {
                    ++i;
                    if (i % 50 == 0)
                    {
                        ipdb.Dispose();
                        ipdb = null;
                        ipdb = new IPDB();
                        // .NET CORE
                        //ipdb.ChangeTracker.AutoDetectChangesEnabled = false; 
                        ipdb.Configuration.AutoDetectChangesEnabled = false;
                    }

                    ipdb.IPAPM_SRVC_NTTN_NODE_MAP.Add(item);
                    ipdb.SaveChanges();
                }                 

1
我建议您查看N.EntityFramework.Extension。它是一个基本的批量扩展框架,适用于EF 6,可在Nuget上获得,并且源代码在Github上以MIT许可证公开。
Install-Package N.EntityFramework.Extensions

https://www.nuget.org/packages/N.EntityFramework.Extensions

一旦安装完成,您可以直接在DbContext实例上使用BulkInsert()方法。它支持批量删除、批量插入、批量合并等操作。 BulkInsert()
var dbcontext = new MyDbContext();  
var orders = new List<Order>();  
for(int i=0; i<10000; i++)  
{  
   orders.Add(new Order { OrderDate = DateTime.UtcNow, TotalPrice = 2.99 });  
}  
dbcontext.BulkInsert(orders);  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接