使用 linq-to-sql 进行批量插入

Posted

技术标签:

【中文标题】使用 linq-to-sql 进行批量插入【英文标题】:bulk insert with linq-to-sql 【发布时间】:2012-02-10 00:08:00 【问题描述】:

我的查询如下所示:

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 不支持批量操作。 我的查询是一次插入每个元素还是一次插入所有元素?

感谢您的澄清。

【问题讨论】:

【参考方案1】:

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

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;

【讨论】:

使用 Linqpad 我必须 t.GetProperties().Where(EventTypeFilter)t.GetFields()。一分钟内插入了 200k 行! (y) 对此赞不绝口。我的 212 条记录的测试插入需要 50 秒,这将其缩短到 1.3 秒。当我实际插入 10k 条记录时应该会有所帮助! 看看这个:***.com/a/21382542/1246870 - 对我来说效果很好 如何在不需要新数据上下文的情况下获取和设置批量插入记录的 ID 属性? 我将 EF 留在 SaveChanges() 上过夜,发现我的 VM 在早上死了。这在 8 秒内插入了 100 万行,谢谢!【参考方案2】:

Bulk Insert 一词通常指的是 SQL Server 特定的基于超快速 bcp 的 SqlBulkCopy 实现。它建立在IRowsetFastLoad 之上。

Linq-2-SQL 在any 条件下不使用这种机制实现插入。

如果您需要将数据批量加载到 SQL Server 并需要它的速度,我建议使用 SqlBulkCopy 进行手动编码。

Linq-2-SQL 将尝试执行一些优化以加速多次插入,但它仍然无法满足许多微 ORM(即使我知道没有实现 SqlBulkCopy 的微 ORM)

【讨论】:

【参考方案3】:

它将为每条记录生成一个插入语句,但会将它们全部发送到单个批处理中的服务器并在单个事务中运行。

这就是循环外的 SubmitChanges() 所做的。

如果你把它移到里面,那么循环中的每次迭代都会去服务器进行 INSERT 并在它自己的事务中运行。

我不相信有任何方法可以触发 SQL BULK INSERT。

【讨论】:

***.com/a/1329094/17174 已经有一段时间没有针对 dapper 测试 l2s insert perf,但我确实测试了 ef 与 dapper ... ef gist.github.com/1623514 的结果非常令人失望【参考方案4】:

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();
                                 

【讨论】:

【参考方案5】:

我建议你看看 N.EntityFramework.Extension。它是 EF 6 的基本批量扩展框架,可在 Nuget 上获得,源代码可在 MIT 许可下在 Github 上获得。

Install-Package N.EntityFramework.Extensions

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

安装后,您可以直接在 DbContext 实例上使用 BulkInsert() 方法。它支持 BulkDelete、BulkInsert、BulkMerge 等。

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);  

【讨论】:

以上是关于使用 linq-to-sql 进行批量插入的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NamedParameterJdbcTemplate 进行批量插入

在 NpgSql 中使用 BeginBinaryImport 插入位数据类型进行批量数据插入

使用 JPA/EJB3 进行批量插入

使用 Node.js/Sequelize 进行批量插入时 PostgreSQL 崩溃

使用 JDBC 和唯一约束进行批量插入

使用 Django REST 框架进行批量插入的最佳设计模式是啥?