【问题标题】:Why does Entity Framework detect changes on properties which were modified but reset?为什么实体框架会检测已修改但重置的属性的更改?
【发布时间】:2011-11-30 18:17:24
【问题描述】:

如果我修改了 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);

你如何处理这个案子?

【问题讨论】:

  • 您的 POCO 实体是动态代理吗(=所有属性都是 virtual)?
  • 我认为这是因为您实际上并没有“重置”该值,而是“重新设置”了它。 EF 如何知道它已更改回原始值,除非它在每次更改值时检查未修改的值?

标签: c# entity-framework entity-framework-4 change-tracking objectstatemanager


【解决方案1】:

如果你所有的属性都是virtual Entity Framework 默认会自动为你的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 没有注意到您两次更改了属性,并认为该属性未更改。

请注意,禁用代理创建 - 如果您在全局范围内执行此操作,例如在上下文构造函数中 - 对您的应用程序来说可能会发生深刻的变化。您的代码可能依赖于动态代理才能正常工作,并且在各种情况下也会严重影响性能。存在动态代理以快速跟踪更改。基于快照的更改跟踪要慢得多。

【讨论】:

  • @Slauma 您还应该提到,如果属性不是虚拟的,则跟踪代理不会对其进行跟踪。这样用户就可以避免在整个上下文中支持 ProxyCreation。
  • @Slauma 属性被标记为已修改,即使在进行赋值调用时它们的值没有更改。这很容易通过在分配属性之前和之后检查 EntityState 来证明它自己的值。所以我认为你的代码示例不准确。问题是“已修改”(即进行了修改尝试)或“已修改”(即原始值和当前值是否不同)之间存在明显的语义差异。 Entity Framework 使用 EntityState 的第一个含义
  • @BradThomas:出于某种原因,直到现在我才看到你的评论。谢谢指正!与此同时,我确信我观察到了你所描述的行为。我已将您的评论编辑为答案。
  • 这是荒谬的行为。怎么没人阻止?
  • “如果你所有的属性都是虚拟的,Entity Framework默认会自动创建你POCO的动态代理”——你有没有参考备份这条语句? (我试图查看文档但无法真正找到)
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2011-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多