为啥 DbSet.Add 工作这么慢?
Posted
技术标签:
【中文标题】为啥 DbSet.Add 工作这么慢?【英文标题】:Why does DbSet.Add work so slow?为什么 DbSet.Add 工作这么慢? 【发布时间】:2011-10-26 05:38:04 【问题描述】:8 个月前在这里讨论了相同的主题:How do I speed up DbSet.Add()?。除了使用我们无法接受的 SqlBulkCopy 之外,没有提出任何解决方案。我决定再次提出它,希望围绕这个问题可能会有新的想法和想法,并提出其他解决方法。至少我只是好奇为什么这个操作需要这么长时间才能运行。
好吧,问题是:我必须将 30K 实体更新到数据库中(EF 4.1,POCO)。实体类型非常简单,包含整数 Id + 其他 4 个整数属性,与其他类型没有关系。 2个案例:
所有这些都是新记录。在 Cntx.Configuration.AutoDetectChangesEnabled=false 的情况下,每个实体一个一个地运行 context.Entities.Add(entity) 需要 90 秒(真值使其永远运行)。然后 SaveChanges 只需一秒钟。其他方法:像这样将其附加到上下文需要相同的 90 秒:
Cntx.Entities.Attach(entity);
Cntx.Entry(entity).State = EntityState.Added;
所有这些都是现有的记录,但有一些变化。在这种情况下,只需几毫秒即可将其附加到现有数据上下文,如下所示:
Cntx.Entities.Attach(entity);
Cntx.Entry(entity).State = EntityState.Modified;
看到区别了吗?
Add 方法的幕后原因是什么让它运行得如此缓慢?
【问题讨论】:
请将您的更新作为答案。 我试过了,但我做不到,需要 100 个声望点(另外 9 个)才能做出自我回答。 赞成你的问题,现在你有 101 个代表 :) 谢谢 :) 我已经把它作为答案 你试过rudi的Cntx.Configuration.AutoDetectChangesEnabled = false;
方法了吗?
【参考方案1】:
我得到了有趣的性能测试结果,我找到了罪魁祸首。在我读过的任何 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;
...
当我发现它时,我开始思考什么可以替代这个 Equals。第一个想法是为 EntityBase 实现 IEquatable,但它碰巧根本没有运行。所以我最终决定做的是为我的模型中的每个具体实体类实现 IEquatable。我只有几个,所以这对我来说是次要的更新。您可以将整个 Equal 操作功能(通常是 2 个对象 Id 比较)放入扩展方法中,以便在具体实体类之间共享并像这样运行它:Equal((EntityBase)ConcreteEntityClass)。最有趣的是,这个 IEquatable 加速了 EntitySet.Add 6 倍!
所以我没有更多的性能问题,相同的代码在不到一秒的时间内为我运行。 我获得了 180 倍的性能提升!太棒了!
结论:
-
运行 EntitySet.Add 的最快方法是为特定实体设置 IEquatable(0.5 秒)
缺少 IEquatable 使其运行 3 秒。
拥有大多数来源推荐的 Equals(object obj) 使其运行时间为 90 秒
【讨论】:
@YMC:能否详细说明如何实现IEquatable<T>
?我对是否为可变类型覆盖 object.Equals() 感到困惑(因为你也应该覆盖 object.GetHashCode(),但是如果在对象在字典中时哈希码发生变化,它就会变成孤立的)。在对象被持久化后,我可以使用数据库中的主键,但在它被持久化之前,所有新对象的键都是 0。我将此作为一个单独的问题提出,但尚未收到良好的响应 ***.com/questions/9782235/…
@YMC 我也希望你能详细说明IEquatable<T>
的实现。我遇到了同样的问题。
vlad 和 Eric,我现在无法访问代码,但我认为您至少可以使用 2 种方法:1) 生成主键值的 Guid 和 GetHashCode() 实现可能很简单类似于return Id.GetHashCode()
2) 如果是整数 Id,它可能看起来像这样IsNew ? base.GetHashCode() : Id.GetHashCode()
,如果 Id==0,IsNew 返回 true。如果方法有问题,请纠正我
哇...这里发生了奇怪的事情...我的代码从 10 秒变为 0.1... cheesus
我试过这个(实现IEquatable<T>
),它对代码的速度没有影响。此外,您谈到 unboxing 但您引用的代码中没有拆箱。在你的结论列表中,选项 #2 并不清楚:它只说明它缺少,但没有说明它是什么。也许一些关于做什么的明确说明会很有用。以上是关于为啥 DbSet.Add 工作这么慢?的主要内容,如果未能解决你的问题,请参考以下文章