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

基本上,我在一个事务中插入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<>正在做一些线性搜索,这需要平方的时间..。

还有人看到这个问题吗?

29288 次浏览

正如 Ladislav 在评论中指出的那样,你需要禁用自动变化检测来提高性能:

context.Configuration.AutoDetectChangesEnabled = false;

这个变化检测是在 DbContext API 中默认启用的。

DbContextObjectContext API 之所以如此不同,是因为当启用自动变化检测时,在内部调用 DetectChanges的函数要比调用 ObjectContext API 的函数多得多。

在这里 你可以找到默认调用 DetectChanges的函数列表,它们是:

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

特别是 Add调用 DetectChanges,这是你所经历的糟糕性能的原因。

与此相反,ObjectContext API 只在 SaveChanges中自动调用 DetectChanges,而在 AddObject和上面提到的其他相应方法中不自动调用 DetectChanges。这就是为什么 ObjectContext违约性能更快的原因。

为什么他们在 DbContext中引入了这么多默认的自动变化检测?我不确定,但似乎禁用它并在适当的地方手动调用 DetectChanges被认为是 高级的,可以很容易地引入微妙的错误到您的应用程序,所以使用[它]小心

使用 EF 4.3进行的小型经验测试代码首先:

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

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

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

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

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

所有这些数据库工作在由用户条目发起的日常插入活动中都是有意义的。但是,如果您只是上传一个现有的数据库,或者有一个生成大量插入的进程。你可能想看看如何加快速度,把它推迟到最后。通常,在插入时禁用索引是一种常见的方法。有非常复杂的优化,可以做取决于情况,他们可能有点势不可挡。

只要知道,在一般情况下插入将比更新时间更长。

在.netcore 2.0中,这被移动到:

AutoDetectChangesEnable = false;