为什么DbSet.Add工作得如此之慢?

时间:2011-08-13 00:21:43

标签: performance entity-framework entity-framework-4.1 poco

8个月前,我们讨论了同一主题:How do I speed up DbSet.Add()?。除了使用我们不接受的SqlBulkCopy之外,没有提出任何解决方案。我决定再次提出这个问题,希望围绕这个问题有新的想法和想法,并提出其他解决方法。至少我只是好奇为什么这个操作需要很长时间才能运行。

嗯,问题是:我必须将30K实体更新到数据库(EF 4.1,POCO)。实体类型非常简单,包含整数Id +其他4个整数属性,与其他类型无关。 2例:

  • 所有这些都是新记录。每个实体逐个运行context.Entities.Add(entity)需要90秒,Cntx.Configuration.AutoDetectChangesEnabled = false(true值使其永久运行)。然后SaveChanges只需要一秒钟。其他方法:将它附加到像这样的上下文需要相同的90秒:

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Added;
    
  • 所有这些都是现有记录,但有一些变化。在这种情况下,将它附加到现有数据上下文只需几毫秒,如下所示:

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Modified;
    

    看到区别?

Add方法的背后是什么让它的工作变得非常慢?

1 个答案:

答案 0 :(得分:27)

我有一些有趣的性能测试结果,我找到了一个罪魁祸首。在我读过的任何EF资源中,我都没有看到这样的信息。

事实证明,在基类中重写了Equals。基类应该包含在所有类型的具体实体之间共享的Id属性。许多EF书籍推荐这种方法并且非常清楚。你可以在这里找到它,例如:How to best implement Equals for custom types?

更确切地说,性能被取消装箱操作(具体类型转换的对象)杀死,这使得它的工作速度很慢。当我评论这行代码时,它需要3秒才能在90秒之前反对!

public override bool Equals ( object obj )
{
    // This line of code made the code so slow 
    var entityBase = obj as EntityBase;
    ...
}

当我发现它时,我开始考虑可能替代这个等于什么。第一个想法是为EntityBase实现IEquatable,但它根本没有运行。所以我最终决定要为我的模型中的每个具体实体类实现IEquatable。我只有少数,所以这对我来说是一个小小的更新。您可以将整个Equal操作功能(通常是2对象ID比较)放入扩展方法,以在具体实体类之间共享并运行它:Equal((EntityBase)ConcreteEntityClass)。最有趣的是,这个IEquatable加速了EntitySet.Add 6次!

所以我没有更多的性能问题,相同的代码运行时间不到一秒钟。 我获得了180倍的性能提升!惊人!

<强>结论

  1. 运行EntitySet.Add的最快方法是为特定实体提供IEquatable(0.5秒)
  2. 缺少IEquatable使其运行3秒。
  3. 大多数消息来源推荐的Equals(object obj)使其运行90秒