与ObjectContext相比,为什么在EF 4.1中插入实体这么慢?


81

基本上,我在一个事务中插入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<>正在执行一些线性搜索,这需要花费大量时间...

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


3
不知何故,我认为答案将类似于此:stackoverflow.com/questions/5917478/...
拉吉斯拉夫Mrnka

Answers:


128

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

context.Configuration.AutoDetectChangesEnabled = false;

默认情况下,DbContextAPI中启用了此更改检测。

DbContext行为与ObjectContextAPI之所以如此不同的原因是,启用自动更改检测后,与API的函数相比,API的内部DbContext调用更多。DetectChangesObjectContext

在这里,您可以找到DetectChanges默认情况下调用的那些函数的列表。他们是:

  • AddAttachFindLocal,或Remove成员DbSet
  • GetValidationErrorsEntrySaveChanges成员DbContext
  • Entries对方法DbChangeTracker

特别是导致您体验不佳的Add呼叫DetectChanges

与此形成对比的是,ObjectContextAPIDetectChanges仅自动调用,SaveChanges而不会自动调用,并且不会自动调用AddObject上面提到的其他相应方法。这就是为什么默认性能ObjectContext更快的原因。

他们为什么DbContext在这么多功能中引入了这种默认的自动更改检测功能?我不确定,但似乎认为禁用它并DetectChanges在适当时点手动调用是高级的,并且可以轻松地将细微的错误引入应用程序,因此请谨慎使用[it]


@Ladislav:没错,我没有找到这个,因为我只是在寻找插入问题:-(
Hartmut

感谢您的解释。我实际上是在调用context.Configuration.AutoDetectChangesEnabled = false,但是我是在Seed()方法的数据库构建过程中做到的。我认为这将设置默认值。我不知道我必须为每个实例调用它。谢谢!
哈特穆特

3
@Hartmut:您可以在派生的DbContext的构造函数中禁用更改检测,然后始终将其禁用。但是就我个人而言,这种禁用状态下“潜在地引入细微错误”的言论使我感到紧张。默认情况下,我启用了更改检测功能,并且仅在像代码块这样的代码块中将其禁用,在这些代码块中,性能提升非常明显,并且我认为它不会引起问题。
Slauma 2011年

我同意,我只是在测试应用程序的一些性能关键部分。在生产代码中,最好将其限制为诸如大容量插入之类的情况
。– Hartmut,

感谢您的回答。那里有很多信息,但这简直就是追逐!
弗雷德·威尔逊

12

EF 4.3 CodeFirst的小经验测试:

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

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

插入1000个具有AutoDetectChanges = true的对象:21秒

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


1
谢谢Zax。根据问题,您得到35,000的结果是什么?您会看到在最初的问题中它表明性能呈二次方下降
Daniel Dyson

9

在.netcore 2.0中,它已移至:

context.ChangeTracker.AutoDetectChangesEnabled = false;


1

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

所有这些数据库工作对于由用户条目发起的每日插入活动都是有意义的。但是,如果您只是上载现有数据库,或者具有一个会生成大量插入内容的过程。您可能需要研究将其推迟到最后的方法。通常在插入时禁用索引是一种常见的方法。根据具体情况可以进行非常复杂的优化,这些优化可能会让人有些不知所措。

只需知道,一般来说插入将比更新花费更长的时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.