2011-04-27 81 views
13

我正在研究mvc3 web應用程序。當用戶更新某些內容時,我想將舊數據與用戶輸入的新數據進行比較,並將每個不同的字段添加到日誌中以創建活動日誌。發現相同類型的兩個實體之間的差異

現在這是我攢動的樣子:

[HttpPost] 
public RedirectToRouteResult SaveSingleEdit(CompLang newcomplang) 
{ 
    var oldCompLang = _db.CompLangs.First(x => x.Id == newcomplang.Id); 

    _db.CompLangs.Attach(oldCompLang); 
    newcomplang.LastUpdate = DateTime.Today; 
    _db.CompLangs.ApplyCurrentValues(newcomplang); 
    _db.SaveChanges(); 

    var comp = _db.CompLangs.First(x => x.Id == newcomplang.Id); 

    return RedirectToAction("ViewSingleEdit", comp); 
} 

我發現我可以利用這個通過我的oldCompLang的屬性迭代:

var oldpropertyInfos = oldCompLang.GetType().GetProperties(); 

但這並不真的因爲它只顯示屬性(Id,Name,Status ...)而不顯示這些屬性的值(1,Hello,Ready ...)。

我只是去了艱辛的道路:

if (oldCompLang.Status != newcomplang.Status) 
{ 
    // Add to my activity log table something for this scenario 
} 

但我真的不想做,對於對象的所有屬性。

我不知道什麼是迭代通過兩個對象來找到不匹配的最佳方式(例如,用戶更改名稱或狀態...),並根據可以存儲在另一個差異中的差異構建列表表。

回答

23

這不是說不好,你可以比較「手動」的屬性使用反射和寫重用的擴展方法 - 你可以以此爲出發點:如果你正在使用的EntityFramework你

public static class MyExtensions 
{ 
    public static IEnumerable<string> EnumeratePropertyDifferences<T>(this T obj1, T obj2) 
    { 
     PropertyInfo[] properties = typeof(T).GetProperties(); 
     List<string> changes = new List<string>(); 

     foreach (PropertyInfo pi in properties) 
     { 
      object value1 = typeof(T).GetProperty(pi.Name).GetValue(obj1, null); 
      object value2 = typeof(T).GetProperty(pi.Name).GetValue(obj2, null); 

      if (value1 != value2 && (value1 == null || !value1.Equals(value2))) 
      { 
       changes.Add(string.Format("Property {0} changed from {1} to {2}", pi.Name, value1, value2)); 
      } 
     } 
     return changes; 
    } 
} 
+0

哇,這工作出色!謝謝! – LanFeusT 2011-04-28 00:51:10

+1

非常有用的小片段 - 謝謝! – rwalter 2013-10-22 17:02:50

+0

我另外,如果財產是你需要的proerties轉換日期時間: '如果(selfValue爲datetime && toValue是DATETIME) { \t \t \t \t \t \t \t \t \t日期時間從=(DateTime的)selfValue; DateTime to =(DateTime)toValue; if(!DateTime.Equals(from,to)) { }' – JoeJoe87577 2015-05-04 13:16:44

0

使用此custom code來比較2個對象。如果他們沒有記錄它。設計觀點在Utility類中創建。

使用它作爲object1.IsEqualTo(object2)

+1

有什麼問題我的答案。 – Praneeth 2011-04-28 05:19:47

0

實現對你的實體IEquatable<T>。另一種方法是實現自定義IComparer<T>,您可以在其中暴露事件,如果屬性不同,將觸發事件。

+0

這是我預期的更復雜。我會想象這是一個相當普遍的問題,將有一個簡單的解決方案^^我甚至不知道如何實現IEquatable 在我的實體上:/ – LanFeusT 2011-04-27 23:31:23

7

可以直接從ObjectContext的

得到改變獲取狀態改變條目:

var modifiedEntries= this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified); 

獲取保存的屬性名稱和原有的和改變的值:

for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount - 1; i++) 
      { 
       var fieldName = stateChangeEntry.OriginalValues.GetName(i); 

       if (fieldName != changedPropertyName) 
        continue; 

       var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString(); 
       var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString(); 
      } 

這比@ BrokenGlass的回答更好,因爲這將在對象圖深去任何改變的狀態,並給你相關的集合的更改的屬性。它也更好,因爲它反映了ObjectContext最終將保存到數據庫的所有內容。使用公認的解決方案,您可能會收到屬性更改,在您通過對象上下文斷開連接的情況下,實際上不會保留屬性更改。

使用EF 4.0,您還可以覆蓋SaveChanges()方法,並將所有審計或活動包含在與最終實體相同的事務中,這意味着在沒有更改實體的情況下審計線索將不存在,反之亦然。這保證了一個準確的日誌。如果你不能保證審計是準確的,那麼它幾乎是無用的。

+0

我喜歡這個答案的外觀,但是我的代碼無法識別ObjectStateManager作爲此方法。難道我難住,你覺得呢? – GP24 2013-02-12 06:30:00

+1

我知道是舊的,但是是一個優秀的解決方案,僅爲那些沒有看到它的兩個細節:1-在你需要一個foreach的兩塊代碼之間(var stateChangeEntry在modifiedEntries中)和2-刪除「-1」 for循環。 – Santiago 2013-09-01 20:06:27

+0

糾正我,如果我錯了,但爲了這個工作,你需要保持你的上下文打開,這意味着你必須爲整個視圖模型使用一個上下文(也就是說,如果你想能夠檢測到多個地方的變化)。只需附加實體也不起作用。在我的情況下,@BrokenGlass解決方案效果更好,因爲我將所有的上下文調用包裝在使用中。 – Bracher 2015-07-29 08:28:03

1

擴展jfar的答案提供一些澄清和變化,我不得不做出得到它的工作:

// Had to add this: 
db.ChangeTracker.DetectChanges(); 
// Had to add using System.Data.Entity.Infrastructure; for this: 
var modifiedEntries = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified); 

foreach (var stateChangeEntry in modifiedEntries) 
{ 
    for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount; i++) 
    { 
     var fieldName = stateChangeEntry.OriginalValues.GetName(i); 
     var changedPropertyName = stateChangeEntry.CurrentValues.GetName(i); 

     if (fieldName != changedPropertyName) 
      continue; 

     var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString(); 
     var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString(); 
     if (originalValue != changedValue) 
     { 
      // do stuff 
      var foo = originalValue; 
      var bar = changedValue; 
     } 

    } 
} 
相關問題