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方法的背后是什么让它的工作变得非常慢?
答案 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倍的性能提升!惊人!
<强>结论强>: