DbContext.ChangeTracker.HasChanges非常慢

时间:2014-06-03 06:38:06

标签: c# entity-framework

我的应用程序使用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实施不是最佳的。 我错过了什么吗?

2 个答案:

答案 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()