2012-11-22 37 views
9

後「實體」狀態「修改」我使用EF5並且不知道爲什麼實體具有狀態「修改」後我設置該實體的唯一更改的PropertyValue返回到原始值。實體框架5 - 爲什麼在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); 
     } 

爲什麼? 。:(

回答

15

因爲實體框架只跟蹤如果數據得到修正,而不是如果它是從它的原始內容的不同

我們用一記漂亮的方法來將狀態恢復到未修改當實體是不變的:

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+是沒有問題的,我已經自己做過了,但是找不到代碼現在

+1

一旦你有'changed = true'''你可以從循環中'斷開',不浪費週期。 –

+0

好點,謝謝。 :)我會添加到我們的實施。 – user1793714

+0

有點神祕地把它放入循環的狀態。 :)一見鍾情,我會想念它。 –

3

Thx for the提示:)

這裏是我的EF5(DbContext)解決方案。我呼籲每DbEnityEntry)這個方法,我從ChangeTracker.Entries得到(

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); 
    } 
+1

請注意您的版本有問題。比較字節數組(blob,rowversion)時,不必使用.Equals()方法。請再次檢查我的版本。如果我的回答對你有幫助,你可以將其標記爲答案。 :)也可以使用(((IObjectContextAdapter)this.dbContext)而不是你的版本。這會給你一個次要的速度優化,但更重要的是:當dbContext不是IObjectContextAdapter而不是NullReferenceException(不太可能,但我寧願程序防守)時,它會拋出一個適當的異常。 – user1793714

+0

@ user1793714 thx :) – user1481065

+1

我相信'entry.OriginalValues.PropertyNames.Any(propertyName =>!entry.OriginalValues [propertyName] .Equals(entry.CurrentValues [propertyName]))'會拋出一個錯誤,如果任何OriginalValue爲null 。應該有一個對OriginalValue爲null的測試,並且CurrentValue不爲null以覆蓋該異常。 – SteveCinq

0

大廈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