我的应用程序使用Entity Framework 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
实施不是最佳的。
我错过了什么吗?
答案 0 :(得分:3)
在第一个代码段中,HasChanges的调用链涉及对DetectChanges的调用。使用快照更改跟踪时,DetectChanges会遍历所有跟踪的实体,以确定是否有任何更改,以便HasChanges将返回正确的结果。
第二个代码片段不会调用DetectChanges,而是向州经理询问它已经知道的状态。因此,如果某个实体已被修改但尚未检测到,则第二个代码段可能会返回错误的结果。
有几种方法可以解决此问题,其中一种方法是使用更改跟踪代理而不是快照更改跟踪。我写了一篇关于DetectChanges的博客系列,详细描述了各种选项和权衡:http://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/。我建议您仔细阅读,以便您可以选择最适合您应用的更改跟踪。
答案 1 :(得分:2)
@Dennis,检测更改枚举所有附加项目。这意味着如果我们添加1000个项目,我们添加的第一个项目不会枚举任何项目,第二个项目会枚举1个项目,依此类推。所以如果我们为此做数学计算,我们得到如下
1 + 2 + 3 + ... + 999 + 1000
or:
N(N+1)/2
在你的情况下,N约为25,000;所以想象总输出的总和。该函数属于O(N ^ 2)类,即大O符号是N的平方的复杂性,这解释了为什么添加大量项目需要这么长时间。
以下代码在3个类别中隔离了25,000个实体,即“已添加”,“已删除”和“已删除”。修改过的&amp;它们的总和仍然是25,000,与前一个返回N(N + 1)/ 2的LOC相反。因此,工作效率比较。
objectStateManager.GetObjectStateEntries(EntityState.Added).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Deleted).Any() ||
objectStateManager.GetObjectStateEntries(EntityState.Modified).Any()