与 ObjectContext 相比,为啥在 EF 4.1 中插入实体如此缓慢?

Posted

技术标签:

【中文标题】与 ObjectContext 相比,为啥在 EF 4.1 中插入实体如此缓慢?【英文标题】:Why is inserting entities in EF 4.1 so slow compared to ObjectContext?与 ObjectContext 相比,为什么在 EF 4.1 中插入实体如此缓慢? 【发布时间】:2011-05-09 22:52:41 【问题描述】:

基本上,我在一个事务中插入 35000 个对象:

using(var uow = new MyContext())
  for(int i = 1; i < 35000; i++) 
     var o = new MyObject()...;
     uow.MySet.Add(o);
  
  uow.SaveChanges();

这需要永远! 如果我使用底层的ObjectContext(通过使用IObjectAdapter),它仍然很慢,但需要大约20秒。看起来DbSet&lt;&gt; 正在做一些线性搜索,这需要大量时间...

还有其他人看到这个问题吗?

【问题讨论】:

不知怎的,我相信答案会是这样的:***.com/questions/5917478/… 【参考方案1】:

正如 Ladislav 在评论中已经指出的那样,您需要禁用自动更改检测以提高性能:

context.Configuration.AutoDetectChangesEnabled = false;

DbContext API 默认启用此更改检测。

DbContext 的行为与 ObjectContext API 如此不同的原因是,当启用自动更改检测时,DbContext API 的更多函数将在内部调用 DetectChanges,而不是 ObjectContext API 的函数。

Here 你可以找到默认调用DetectChanges 的那些函数的列表。它们是:

DbSet 上的 AddAttachFindLocalRemove 成员@ GetValidationErrorsEntrySaveChanges 成员在 DbContext DbChangeTracker 上的 Entries 方法

尤其是Add 调用DetectChanges 是造成您遇到的性能不佳的原因。

与此相反,ObjectContext API 仅在 SaveChanges 中自动调用 DetectChanges,但在 AddObject 和上述其他相应方法中不会自动调用。这就是ObjectContext默认性能更快的原因。

为什么他们在DbContext 的这么多功能中引入了这种默认的自动更改检测?我不确定,但似乎禁用它并在适当的位置手动调用DetectChanges 被视为advanced and can easily introduce subtle bugs into your application so use [it] with care。

【讨论】:

@Ladislav:你说得对,我没有找到这个,因为我只是在寻找插入问题:-( 感谢您的解释。我实际上是在调用 context.Configuration.AutoDetectChangesEnabled = false ,但我是在 Seed() 方法中的数据库构建期间调用的。我认为这将设置默认值。我不知道我必须为每个实例调用它。谢谢! @Hartmut:您可以在派生的 DbContext 的构造函数中禁用更改检测,然后始终禁用它。但就个人而言,当它被禁用时,关于“可能引入微妙的错误”的评论让我感到紧张。默认情况下,我启用了更改检测,并且仅在像您这样的代码块中禁用它,其中性能提升明显并且我觉得它不会引起问题。 我同意,我只是在测试我的应用程序的一些性能关键部分。在生产代码中,最好将其限制为批量插入等情况。 感谢您的回答。那里有很多信息,但这切入正题!【参考方案2】:

EF 4.3 CodeFirst 的小经验测试:

使用 AutoDetectChanges = true 移除 1000 个对象:23 秒

使用 AutoDetectChanges = false 移除 1000 个对象:11 秒

使用 AutoDetectChanges = true 插入 1000 个对象:21 秒

使用 AutoDetectChanges = false 插入 1000 个对象:13 秒

【讨论】:

谢谢扎克斯。根据问题,您的 35,000 的结果是什么?您会看到,在原始问题中,性能呈二次方下降【参考方案3】:

在 .netcore 2.0 中,它被移至:

context.ChangeTracker.AutoDetectChangesEnabled = false;

【讨论】:

【参考方案4】:

除了您在这里找到的答案。重要的是要知道,在数据库级别,插入比添加更多的工作。数据库必须扩展/分配新空间。然后它必须至少更新主键索引。尽管更新时索引也可能会更新,但这种情况并不常见。如果有任何外键,它也必须读取这些索引以确保保持引用完整性。触发器也可以发挥作用,尽管它们会以同样的方式影响更新。

所有这些数据库工作在由用户条目发起的日常插入活动中都是有意义的。但是如果你只是上传一个现有的数据库,或者有一个生成大量插入的过程。您可能想通过将其推迟到最后来寻找加快速度的方法。通常在插入时禁用索引是一种常见的方式。可以根据情况进行非常复杂的优化,它们可能有点压倒性。

只要知道通常插入会比更新花费更长的时间。

【讨论】:

以上是关于与 ObjectContext 相比,为啥在 EF 4.1 中插入实体如此缓慢?的主要内容,如果未能解决你的问题,请参考以下文章

向下转换 ObjectContext 到实体集合

此 ObjectContext 实例已释放,不可再用于需要连接的操作。

EF + UnitOfWork + SharePoint RunWithElevatedPrivileges

efcore为啥代码量大

使用 ObjectContext 获取关系记录的主键

如果将新对象添加到 ObjectSet,则 ObjectContext.SaveChanges() 永远不会返回。有没有人经历过这个?