为什么实体框架检测属性的变化,修改但重置

本文关键字:修改 变化 实体 框架 检测 属性 为什么 | 更新日期: 2023-09-27 18:16:29

如果我修改POCO实体的属性,但重置它,EntityFramework仍然说有变化。

Property "Name": Value "Test" (original value) 
              -> Value "Test123" (value changed by UI) 
              -> Value "Test" (value changed by UI to original value)

已修改的表项:

var objectStateEntries = 
    _db.ObjectStateManager.GetObjectStateEntries(
        EntityState.Added | 
        EntityState.Deleted | 
        EntityState.Modified);

你如何处理这种情况?

为什么实体框架检测属性的变化,修改但重置

如果你所有的属性都是virtual实体框架将自动创建一个动态代理你的POCO默认情况下。如果我没记错的话,变化跟踪在这种情况下是基于这个动态对象的属性设置的,大致如下:

private string _name;
public string Name
{
    // ...
    set
    {
        // if (_name != value) such a check probably does not happen
        {
            _name = value;
            MarkPropertyAsModified(...);
        }
    }
}

因此,不与原始值进行比较,而只与属性的当前值进行比较。如果这个值被改变了,这个属性就会被标记为已修改,不管你是否将它重置回原来的值。

(对上一段的编辑和更正:如果调用setter,则属性被标记为Modified,无论赋值是相同的还是已更改的值。感谢Brad Thomas和他在下面的评论!)

您可以通过在上下文选项中禁用此功能来避免创建动态代理:

objectContext.ContextOptions.ProxyCreationEnabled = false;

变更检测现在将依赖于快照的创建,这意味着当变更检测被调用时,EF将原始值(存储在对象上下文中的快照中)与当前值进行比较。这在属性设置中不再发生,而是在实体框架的某些功能中发生,例如在SaveChanges中。在您的情况下,这意味着当您调用SaveChanges原始值(快照)和当前值将是相同的,因为您重置了更改。基本上EF没有注意到你修改了两次属性,并认为属性没有改变。

请注意,禁用代理创建—如果您全局地这样做,例如在上下文构造函数中—可能会对您的应用程序造成很大的改变。您的代码可能依赖于动态代理才能正常工作,这也可能严重影响各种情况下的性能。动态代理的存在是为了快速跟踪更改。基于快照的变更跟踪要慢得多。

使用动态代理对象,一旦更改属性值,上下文将其标记为已更改