DbContext的AutoDetectChangesEnabled属性设置为false时如何检测更改

55

我有些困惑。从我读到的内容来看,将 DbContext.AutoDetectChangesEnabled 设置为 false 应该禁用更改跟踪,需要调用 DbContext.DetectChanges 来识别要发送到数据库的更改。

然而,从下面的日志中可以清楚地看到,即使设置为 false,dbContext 的更改跟踪器仍在注册更改。

我错过了什么吗?

Entity Framework 版本:5.0.0.0

DbContext 类

public class ProjectContext : DbContext {
    public DbSet<Project> Projects {get;set;}
}

控制器类

private ProjectContext db = new ProjectContext();

public method(){
    Project p = new Project("uniqueName");
    db.Configuration.AutoDetectChangesEnabled = false;
    db.Projects.Add(p);
    DebugChangeTracker();
    db.SaveChanges();

    db.Projects.First().ProjectName = "a differentName!";
    DebugChangeTracker();
    db.SaveChanges();
}

记录方法

    private void DebugChangeTracker()
    {
        var path = "C:\\mypath\\";
        path = path + Util.GetMsSinceEpoch().ToString() + "changeTracker.log";

        using (StreamWriter sw = new StreamWriter(path))
        {
            var changeTracker = db.ChangeTracker;
            var entries = changeTracker.Entries();
            foreach (var x in entries)
            {

                var name = x.Entity.ToString();
                var state = x.State;

                sw.WriteLine("");
                sw.WriteLine("***Entity Name: " + name +
                             "is in a state of " + state);
                var currentValues = x.CurrentValues;
                sw.WriteLine("***CurrentValues***");
                PrintPropertyValues(currentValues,sw);
                if (state != EntityState.Added)
                {
                    sw.WriteLine("***Original Values***");
                    PrintPropertyValues(x.OriginalValues,sw);
                }
            }
        }
    }

第一条日志

***Entity Name: Models.Projectis in a state of Added
***CurrentValues***
ProjectId:0
ProjectName:uniqueName

第二个日志

***Entity Name: Models.Projectis in a state of Modified
***CurrentValues***
ProjectId:1
ProjectName:uniqueName
***Original Values***
ProjectId:1
ProjectName:a differentName!
4个回答

68
AutoDetectChangesEnabled设置为false并不会禁用更改跟踪。(这就是AsNoTracking()扩展方法的作用。)它只是禁用了许多DbContext API方法中本来会发生的自动调用DetectChanges的操作。但是,DetectChanges并不是唯一参与更改跟踪的方法。然而,如果您不在需要使用它的正确位置手动调用它,则跟踪的实体状态将不完整或错误,导致数据保存不正确。在您的情况下,在method的第一部分中期望出现状态Added,即使AutoDetectChangesEnabled设置为false,因为您只调用了db.Projects.Add(p)。(顺便说一句,您的代码中缺少这一行,但我猜这只是一个复制粘贴错误。)从DbContext API调用方法可以正确跟踪更改,并且如果在调用Add之前状态正确,则跟踪器中的状态将是正确的。
换句话说,调用API方法不会将正确状态变为错误状态。但是,如果AutoDetectChangesEnabled为false,则它也不会将错误状态变为正确状态,如果AutoDetectChangesEnabled为true,则会发生这种情况。
然而,在您方法的第二部分中,您只是更改了POCO属性值。在此点之后,更改跟踪器状态是错误的(未更改),而没有调用DetectChanges(手动或自动 - 如果AutoDetectChangesEnabled为true - 自动在ChangeTracker.Entries或SaveChanges中),它永远不会被调整。其效果是更改的属性值未保存到数据库中。
在提到状态Unchanged的最后一部分中,我指的是我的自己的测试(以及我预期的内容)。我不知道您为什么有“Modified”状态,并且无法重现。
抱歉,如果这听起来有点混乱。Arthur Vickers可以更好地解释。 我发现自动更改检测以及禁用它时的行为很难理解和掌握,通常我不会触及默认值( AutoDetectChangesEnabled = true ),对于任何比最简单的事情(如在循环中批量添加实体等)更复杂的跟踪更改。

我不得不多读几遍,但这确实有助于回答我的问题,非常感谢!抱歉复制粘贴错误;为了后人,我会更新问题。 - Jesse
2
不幸的是,“在循环中批量添加实体”时,您需要禁用更改跟踪。这是一个_巨大的_加速(样本大小为1,在我的应用程序中进行了测试,但这是两次运行之间添加约3000行的唯一区别)。 - Ed S.
2
@EdS:批量添加是我所指的“最简单的事情”之一,我实际上禁用自动变更检测。 - Slauma
可能有用的链接:Entity Framework自动检测更改 - Iman Mahmoudinasab
如何进行模拟? - Sana Ahmed

34

如果有人在Entity Framework Core中寻找AutoDetectChangesEnabled,则可以在ChangeTracker下找到它,而不是在Configuration下。

用法如下:

context.ChangeTracker.AutoDetectChangesEnabled = false;

//Do something here
context.PriceRecords.Add(newPriceRecord);

context.ChangeTracker.AutoDetectChangesEnabled = true;

1
谢谢,这正是我在寻找的。 - Daniel
在配置文件中搜索了这么多小时。谢谢。 - Sagar Khatri

10

根据实体框架自动检测更改的文章

他们说:

在某些情况下,通过将其关闭可以获得显着的性能提升(有关示例,请参见该文章)

看一下来自该文章的示例

using (var context = new BloggingContext()) 
{ 
    try 
    { 
        context.Configuration.AutoDetectChangesEnabled = false; 

        // Make many calls in a loop 
        foreach (var blog in aLotOfBlogs) 
        { 
            context.Blogs.Add(blog); 
        } 
    } 
    finally 
    { 
        context.Configuration.AutoDetectChangesEnabled = true; 
    }
}

这段代码避免了在调用 DbSet.AddSaveChanges 方法时不必要地调用 DetectChanges

5
如果你在finally块中重新打开它,那么它会自动执行它本来应该执行的操作但更快吗? - Ian Warburton

0
我关闭了AutoDetectChangesEnabled后,发现性能大幅提升。
我在一个列表中添加了数千条记录,关闭这个选项后,运行时间从几个小时缩短到几分钟。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接