使用 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 插入位数据类型进行批量数据插入