【问题标题】:Entity Framework 5 - Why is Entity State "Modified" after PropertyValue is set back to Original实体框架 5 - 为什么在 PropertyValue 设置回原始后实体状态“已修改”
【发布时间】:2017-01-15 02:09:01
【问题描述】:

我使用 EF5,但不知道为什么在我将此实体的唯一更改的 PropertyValue 设置回原始值后,该实体的状态为“已修改”。

using (TestDbContext context = new TestDbContext())
        {
            string name = context.Person.First().Name;

            // count is 0
            int count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified);

            // Change Value
            context.Person.First().Name = "Test";

            // count is 1 
            count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified);

            // Revert Value
            context.Person.First().Name = name;


            context.ChangeTracker.DetectChanges();

            // count is 1 
            count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified);
        }

为什么? :(

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    因为 Entity Framework 只跟踪数据是否被修改,而不是如果它与原始内容不同。

    当实体未更改时,我们使用一种漂亮的方法将状态重置为未修改:

        public static void CheckIfModified(EntityObject entity, ObjectContext context)
        {
            if (entity.EntityState == EntityState.Modified)
            {
                ObjectStateEntry state = context.ObjectStateManager.GetObjectStateEntry(entity);
                DbDataRecord orig = state.OriginalValues;
                CurrentValueRecord curr = state.CurrentValues;
    
                bool changed = false;
                for (int i = 0; i < orig.FieldCount && !changed; ++i)
                {
                    object origValue = orig.GetValue(i);
                    object curValue = curr.GetValue(i);
                    if (!origValue.Equals(curValue) && (!(origValue is byte[]) || !((byte[])origValue).SequenceEqual((byte[])curValue)))
                    {
                        changed = true;
                    }
                }
    
                if (!changed)
                {
                    state.ChangeState(EntityState.Unchanged);
                }
            }
        }
    

    请注意,此方法适用于 EF 4.0,不适用于带有 DbContext 的较新版本。但是用EF 4.1+重写是没有问题的,我自己已经做过了,但是现在找不到代码。

    【讨论】:

    • 一旦你有了changed = true;,你就可以从循环中break,以免浪费循环。
    • 好点,谢谢。 :) 我会把它添加到我们的实现中。
    • 将其置于循环状态有点神秘。 :) 乍一看我会想念它。
    • 哦..是的..我也忘记了。可能仍然来自我上学的时候,他们总是告诉我们:休息是邪恶的!
    • 除了 byte[] 比较情况外,您还应该检查字符串,因为 99% 的时间您都不想考虑 null 和 string.Empty 的变化。
    【解决方案2】:

    谢谢你的提示:)

    这是我的 EF5 (DbContext) 解决方案。我为从 ChangeTracker.Entries() 获得的每个 DbEnityEntry 调用此方法

        private void CheckIfDifferent(DbEntityEntry entry)
        {
            if (entry.State != EntityState.Modified) 
                return;
    
            if (entry.OriginalValues.PropertyNames.Any(propertyName => !entry.OriginalValues[propertyName].Equals(entry.CurrentValues[propertyName])))
                return;
    
           (this.dbContext as IObjectContextAdapter).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).ChangeState(EntityState.Unchanged);
        }
    

    【讨论】:

    • 请注意您的版本有问题。在比较字节数组(blob、rowversion)时,您不必使用 .Equals() 方法。请再次检查我的版本。如果我的回答对您有所帮助,您可以将其标记为答案。 :) 也使用 ((IObjectContextAdapter)this.dbContext) 代替您的版本。这将为您提供较小的速度优化,但更重要的是:当 dbContext 不是 IObjectContextAdapter 而不是 NullReferenceException 时,它将引发适当的异常(不太可能,但我宁愿编程防御)。
    • 我相信entry.OriginalValues.PropertyNames.Any(propertyName =&gt; !entry.OriginalValues[propertyName].Equals(entry.CurrentValues[propertyName])) 会在任何 OriginalValue 为空时抛出错误。应该对 OriginalValue 为 null 和 CurrentValue 不为 null 进行测试以覆盖异常。
    【解决方案3】:

    基于 User1481065 的回答,如果OriginalValues 中的任何一个为空,则要克服异常的可能性,请尝试以下操作。我假设一个上下文可能包含多个可能有更新的实体(可能不一定是值的实际变化)。

    _dirty = False
    Dim oChanges As IEnumerable(Of DbEntityEntry(Of CsSetting)) = _dbContext.ChangeTracker.Entries(Of CsSetting)().Where(Function(r) r.State <> EntityState.Unchanged)
    For Each c As DbEntityEntry(Of CsSetting) In oChanges
        _dirty = c.OriginalValues.PropertyNames.Any(Function(n) (c.OriginalValues(n) Is Nothing And c.CurrentValues(n) IsNot Nothing) OrElse (c.OriginalValues(n) IsNot Nothing AndAlso Not c.OriginalValues(n).Equals(c.CurrentValues(n))))
        If _dirty Then Exit For
    Next c
    Return _dirty
    

    您可能不需要循环,因此不需要 _dirty 的预设。

    【讨论】:

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