DbContext.ChangeTracker.HasChanges is very slow
本文关键字:very slow is HasChanges ChangeTracker DbContext | 更新日期: 2023-09-27 18:06:37
我的应用程序使用实体框架6.1.0和DbContext
API。
这是一种CAD系统,用于编辑一些工程文档。为了检测文档中的更改,我使用DbContext.ChangeTracker.HasChanges
。
当文档有大量数据(大约20-25个实体)时,DbContext.ChangeTracker.HasChanges
运行非常慢。由于此代码用于启用/禁用"保存"命令,因此它经常从UI线程执行。这反过来又会影响应用程序的性能。
我已经重写了这个片段:
private Lazy<DbContext> context;
public bool HasChanges
{
get
{
if (!context.IsValueCreated)
{
return false;
}
return context.Value.ChangeTracker.HasChanges();
}
}
:
public bool HasChanges
{
get
{
if (!context.IsValueCreated)
{
return false;
}
var objectStateManager = ((IObjectContextAdapter)context.Value).ObjectContext.ObjectStateManager;
return
objectStateManager.GetObjectStateEntries(EntityState.Added).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Deleted).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Modified).Any();
}
}
和(这是一个奇迹!)一切都运行得非常快。
看起来DbChangeTracker.HasChanges
的实现不是最优的。我错过什么了吗?
在第一个代码片段中,对HasChanges的调用链涉及到对DetectChanges的调用。当使用快照更改跟踪时,DetectChanges将遍历所有被跟踪的实体,以确定是否有任何更改,以便HasChanges将返回正确的结果。
第二个代码片段没有调用DetectChanges,而只是询问状态管理器它已经知道的状态。因此,如果实体已被修改,但尚未检测到,则第二个代码片段可能返回错误的结果。
有几种方法可以处理这个问题,其中之一是使用变更跟踪代理而不是快照变更跟踪。我在DetectChanges上写了一个博客系列,详细描述了各种选项和权衡:http://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/。我建议你通读一遍,这样你就可以很好地选择哪种类型的更改跟踪最适合你的应用程序。@Dennis,检测更改枚举所有附加项。这意味着如果我们添加1000个项目,我们添加的第一个项目不枚举任何项目,第二个项目枚举1个项目,以此类推。因此,如果我们对此进行计算,我们得到如下
1 + 2 + 3 + ... + 999 + 1000
or:
N(N+1)/2
在你的例子中N大约是25000;想象一下总的输出和。这个函数属于O(N^2)类,也就是说,大O符号是N的平方的复杂度,这就解释了为什么添加大量项需要这么长时间。
如下代码将25,000个实体划分为3类,即添加、删除和;修改后的,它们的总和仍然是25,000,与之前返回N(N+1)/2的1个LOC相反。因此工作效率比较。
objectStateManager.GetObjectStateEntries(EntityState.Added).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Deleted).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Modified).Any()