首先需要澄清几点:
对于基于批处理的操作,EF 并不比其他方法慢很多。在我的测试中,使用原始 SQL 命令可能会提高 50%,使用 SQL 批量复制可能会快 10 倍,但作为一般规则,EF 并不比比较方法慢很多(尽管通常被认为非常慢)。对于大多数应用程序,即使在批处理场景中,只要进行正确的调整,EF 也会给出合适的性能数字。 (请参阅我的文章:http://blog.staticvoid.co.nz/2012/3/24/entity_framework_comparative_performance and http://blog.staticvoid.co.nz/2012/8/17/mssql_and_large_insert_statements)
由于 EF 更改跟踪的方式,它有可能远远超过大多数人编写基于 SqlCommand 的插入语句的性能(查询计划、往返和事务有很多问题,这使得编写起来非常困难)最佳地执行批量插入语句)。我在这里向 EF 提出了这些补充(http://entityframework.codeplex.com/discussions/377636)但尚未实施。
您关闭自动检测更改的决定是完全正确的,每个带有检测更改的 .Add 或 .Attach 操作都会枚举跟踪图,因此,如果您在同一上下文中添加 17k 添加项,则需要枚举图 17000总共 17000 + 16999 + ...+ 2 + 1 = 144,500,000 个实体,难怪要花这么长时间,对吧? (请参阅我的文章:http://blog.staticvoid.co.nz/2012/5/7/entityframework_performance_and_autodetectchanges)
保存更改始终需要枚举跟踪图(它在内部调用检测更改),因此您的第一种方法会很慢,因为它实际上会执行与上面相同数量的跟踪调用。
第二种方法要好得多,但它仍然有一个相当大的缺陷,我认为这是双重的,首先,当您保存更改时,图形非常大(图形越大,跟踪时间呈指数级增长),其次,它会占用需要大量内存来一次性保存整个图,特别是考虑到 EF 存储每个实体的两个副本。
更好的方法是以块的形式保存图表。一些
//With Auto detect changes off.
foreach(var batch in batches)//keep batch size below 1000 items, play around with the numbers a little
{
using(var ctx = new MyContext())//make sure you create a new context per batch.
{
foreach(var entity in batch){
ctx.Entities.Add(entity);
}
ctx.SaveChanges();
}
}
我预计您应该以 17-30 秒左右的时间来完成所有 17k 行。
通过使用原始 SQL 命令执行此操作,您可能可以将其缩短到 12-20 秒左右;
通过批量复制重新实现,您可能可以将其缩短到 2-5 秒