2011-04-27 10 views
13

मैं एक एमवीसी 3 वेब ऐप पर काम कर रहा हूं। जब उपयोगकर्ता कुछ अपडेट करता है, तो मैं पुराने डेटा की तुलना उस नए उपयोगकर्ता से करना चाहता हूं जो उपयोगकर्ता इनपुट कर रहा है और प्रत्येक फ़ील्ड के लिए अलग-अलग गतिविधि लॉग बनाने के लिए लॉग में जोड़ें।उसी प्रकार की दो इकाइयों के बीच अंतर खोजें

[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(); 

लेकिन यह वास्तव में नहीं है

अभी इस क्या मेरी कार्रवाई को बचाने की तरह लग रहा है मदद करें क्योंकि यह केवल मुझे गुण (आईडी, नाम, स्थिति ...) दिखाता है और इन गुणों के मान नहीं (1, हैलो, तैयार ...)।

मैं सिर्फ मुश्किल तरीके से जा सकते हैं:

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

+1

बहुत उपयोगी छोटी स्निपेट - धन्यवाद! – rwalter

+0

मेरे अलावा, अगर संपत्ति एक datetime आप proerties बदलने की आवश्यकता है: 'अगर (selfValue दिनांक समय && toValue है दिनांक समय है) { \t \t \t \t \t \t \t \t \t DATETIME से = (DATETIME) selfValue; डेटटाइम = (डेटटाइम) toValue; यदि (! DateTime.Equals (से, से)) { } ' – JoeJoe87577

0

2 ऑब्जेक्ट्स की तुलना करने के लिए इस custom code का उपयोग करें। अगर वे इसे लॉग इन करने में विफल रहते हैं। दृश्य बिंदु डिजाइन उपयोगिता वर्ग में इसे बनाएँ।

object1.IsEqualTo(object2)

+1

मेरे जवाब के साथ गलत है। – Praneeth

0

के रूप में यह प्रयोग को लागू करें तो अपनी इकाई पर IEquatable<T>। एक और तरीका कस्टम IComparer<T> लागू कर रहा है, जहां आप उदाहरण के लिए घटना का पर्दाफाश कर सकते हैं, अगर संपत्ति अलग हो तो निकाल दिया जाएगा।

+0

यह बहुत अधिक जटिल है कि मुझे उम्मीद थी। मैंने यह कल्पना की होगी कि यह एक बहुत ही आम मुद्दा होगा जिसका अर्थ है ^^ मुझे यह भी सुनिश्चित नहीं है कि मेरी इकाई पर IEquatable को कैसे कार्यान्वित किया जाए:/ – LanFeusT

7

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

बचाया संपत्ति के नाम हो रही है और मूल और बदल मूल्यों: राज्य परिवर्तन प्रविष्टियों हो रही ObjectContext

से सीधे परिवर्तन प्राप्त कर सकते हैं :

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 के जवाब की तुलना में बेहतर है, क्योंकि यह किसी भी बदली हुई राज्यों के लिए वस्तु ग्राफ में गहरी जाना होगा और आप संबद्ध संग्रह में से बदल गुण दे देंगे। यह भी बेहतर है क्योंकि यह ऑब्जेक्ट कॉन्टेक्स्ट डेटाबेस को सहेजने के बाद सबकुछ दर्शाता है। स्वीकार्य समाधान के साथ आपको संपत्ति में परिवर्तन मिल सकते हैं जो वास्तव में उस स्थिति में बने रहेंगे जहां आप ऑब्जेक्ट संदर्भ के माध्यम से डिस्कनेक्ट हो जाते हैं।

ईएफ 4.0 के साथ आप SaveChanges() विधि को ओवरराइड भी कर सकते हैं और उसी लेनदेन में किसी भी ऑडिटिंग या गतिविधि को लपेट सकते हैं क्योंकि अंतिम इकाई को ऑडिट ट्रेल का अर्थ सहेजने के बिना अस्तित्व में नहीं बदला जा सकता है और इसके विपरीत। यह एक सटीक लॉग की गारंटी देता है। यदि आप ऑडिट की गारंटी नहीं दे सकते हैं तो यह लगभग बेकार है।

+0

मुझे इस उत्तर को देखना पसंद है लेकिन मेरा कोड ऑब्जेक्टस्टेट प्रबंधक को इस विधि के रूप में नहीं पहचाना जाएगा। क्या मुझे स्टंप किया गया है, आपको क्या लगता है? – GP24

+1

मुझे पता है कि पुराना है लेकिन एक उत्कृष्ट समाधान है, केवल उन लोगों के लिए दो विवरण इसे नहीं देख पाए: 1- कोड के 2 ब्लॉक के बीच आपको एक foreach (संशोधित एंटर्रीज़ में var stateChangeEntry) की आवश्यकता है और 2- "-1" को हटाएं लूप के लिए। – Santiago

+0

अगर मैं गलत हूं तो मुझे सही करें, लेकिन इसके लिए आपको अपना संदर्भ खुला रखना होगा, जिसका अर्थ है कि आपको अपने संपूर्ण व्यूमोडेल के लिए एक संदर्भ का उपयोग करना होगा (यही है कि आप कई स्थानों में परिवर्तनों को पहचानने में सक्षम होना चाहते हैं)। बस इकाई को जोड़ना या तो काम नहीं करता है। मेरे मामले में @ ब्रोकन ग्लास समाधान बेहतर काम करता है क्योंकि मैं अपने सभी संदर्भ कॉल को उपयोग में लपेटता हूं। – Bracher

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; 
     } 

    } 
} 
संबंधित मुद्दे