2011-05-27 13 views
39

मैं कक्षाओं के एक समूह में संपत्तियों में संपत्ति परिवर्तनों के एक बाध्य 'ऑडिट लॉग' को लागू करने की कोशिश कर रहा हूं। मैंने सफलतापूर्वक पाया है कि कैसे बनाया गया है | ModifiedOn प्रकार गुणों को सेट करने के लिए, लेकिन यह संशोधित करने में विफल रहा है कि संशोधित की गई संपत्ति को 'ढूंढें' कैसे।इकाई फ्रेमवर्क 4.1 डीबीकॉन्टेक्स्ट सेव चेंज को ऑडिट संपत्ति में बदलने के लिए ओवरराइड करें

उदाहरण:

public class TestContext : DbContext 
{ 
    public override int SaveChanges() 
    { 
     var utcNowAuditDate = DateTime.UtcNow; 
     var changeSet = ChangeTracker.Entries<IAuditable>(); 
     if (changeSet != null) 
      foreach (DbEntityEntry<IAuditable> dbEntityEntry in changeSet) 
      { 

       switch (dbEntityEntry.State) 
       { 
        case EntityState.Added: 
         dbEntityEntry.Entity.CreatedOn = utcNowAuditDate; 
         dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate; 
         break; 
        case EntityState.Modified: 
         dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate; 
         //some way to access the name and value of property that changed here 
         var changedThing = SomeMethodHere(dbEntityEntry); 
         Log.WriteAudit("Entry: {0} Origianl :{1} New: {2}", changedThing.Name, 
             changedThing.OrigianlValue, changedThing.NewValue) 
         break; 
       } 
      } 
     return base.SaveChanges(); 
    } 
} 

तो, वहाँ संपत्ति है कि एफई 4.1 DbContext में विवरण के स्तर के साथ बदल पहुंचने का एक तरीका है?

उत्तर

44

बहुत, बहुत मोटा अनुमान:

foreach (var property in dbEntityEntry.Entity.GetType().GetProperties()) 
{ 
    DbPropertyEntry propertyEntry = dbEntityEntry.Property(property.Name); 
    if (propertyEntry.IsModified) 
    { 
     Log.WriteAudit("Entry: {0} Original :{1} New: {2}", property.Name, 
      propertyEntry.OriginalValue, propertyEntry.CurrentValue); 
    } 
} 

अगर यह वास्तव में विस्तार से काम करेगा मैं कोई सुराग नहीं है, लेकिन यह कुछ मैं पहले कदम के रूप की कोशिश करेंगे है। निस्संदेह एक संपत्ति हो सकती है जो बदल गई है, इसलिए लूप और शायद WriteAudit की कई कॉल।

SaveChanges के अंदर प्रतिबिंब सामग्री हालांकि प्रदर्शन प्रदर्शन दुःस्वप्न बन सकती है।

संपादित

शायद यह अंतर्निहित ObjectContext उपयोग करने के लिए बेहतर है। फिर कुछ ऐसा संभव है:

public class TestContext : DbContext 
{ 
    public override int SaveChanges() 
    { 
     ChangeTracker.DetectChanges(); // Important! 

     ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext; 

     List<ObjectStateEntry> objectStateEntryList = 
      ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added 
                 | EntityState.Modified 
                 | EntityState.Deleted) 
      .ToList(); 

     foreach (ObjectStateEntry entry in objectStateEntryList) 
     { 
      if (!entry.IsRelationship) 
      { 
       switch (entry.State) 
       { 
        case EntityState.Added: 
         // write log... 
         break; 
        case EntityState.Deleted: 
         // write log... 
         break; 
        case EntityState.Modified: 
        { 
         foreach (string propertyName in 
            entry.GetModifiedProperties()) 
         { 
          DbDataRecord original = entry.OriginalValues; 
          string oldValue = original.GetValue(
           original.GetOrdinal(propertyName)) 
           .ToString(); 

          CurrentValueRecord current = entry.CurrentValues; 
          string newValue = current.GetValue(
           current.GetOrdinal(propertyName)) 
           .ToString(); 

          if (oldValue != newValue) // probably not necessary 
          { 
           Log.WriteAudit(
            "Entry: {0} Original :{1} New: {2}", 
            entry.Entity.GetType().Name, 
            oldValue, newValue); 
          } 
         } 
         break; 
        } 
       } 
      } 
     } 
     return base.SaveChanges(); 
    } 
} 

मैंने इसे स्वयं ईएफ 4.0 में उपयोग किया है। मुझे DbContext एपीआई में GetModifiedProperties (प्रतिबिंब कोड से बचने की कुंजी) के लिए एक संबंधित विधि नहीं मिल रही है।

संपादित 2

महत्वपूर्ण: जब POCO संस्थाओं के साथ काम कर कोड के ऊपर शुरुआत में DbContext.ChangeTracker.DetectChanges() कॉल करने के लिए की जरूरत है। कारण यह है कि base.SaveChanges को यहां बहुत देर हो चुकी है (विधि के अंत में)। base.SaveChanges आंतरिक रूप से DetectChanges पर कॉल करता है, लेकिन क्योंकि हम पहले परिवर्तनों का विश्लेषण और लॉग इन करना चाहते हैं, हमें मैन्युअल रूप से DetectChanges पर कॉल करना होगा ताकि ईएफ सभी संशोधित गुणों को ढूंढ सके और राज्यों को परिवर्तन ट्रैकर में सही ढंग से सेट कर सके।

संभव स्थितियों में, जहां कोड, DetectChanges बुला बिना काम कर सकते हैं, उदाहरण के लिए Add या Remove तरह DbContext/DbSet तरीकों का इस्तेमाल किया जाता है, तो बाद पिछले संपत्ति संशोधन किया जाता है के बाद से इन तरीकों में भी आंतरिक रूप से DetectChanges फोन कर रहे हैं। लेकिन अगर उदाहरण के लिए एक इकाई सिर्फ डीबी से लोड की जाती है, तो कुछ गुण बदल जाते हैं और फिर यह SaveChanges व्युत्पन्न होता है, स्वचालित परिवर्तन पहचान base.SaveChanges से पहले नहीं होती है, जिसके परिणामस्वरूप संशोधित गुणों के लिए लॉग प्रविष्टियां गायब होती हैं।

मैंने तदनुसार उपरोक्त कोड अपडेट किया है।

+0

यह काम करता है, लेकिन ऐसा लगता है कि सभी गुण इस के अनुसार संशोधित हैं, जोड़ने के लिए विस्तृत लेखापरीक्षा। आगे की जांच करेंगे, लेकिन मूल प्रश्न के लिए ठीक है। मुझे लगता है कि यह मुझे जो चाहिए वह देता है। –

+0

यह मेरे लिए काम नहीं करता है। पुराने मूल्य हमेशा खाली होते हैं। क्या यह केवल पीओसीओ और 4.1 और ऊपर काम कर रहा है? – Jonx

+0

@ जोनक्स: हाँ, यह ईएफ 4.1 ('डीबीकॉन्टेक्स्ट') और पीओसीओ है। आप <= EF 4.0 ('ऑब्जेक्ट कॉन्टेक्स्ट') के लिए कुछ ऐसा ही कर सकते हैं, केवल अंतर यह है कि आप 'सेव चेंज' को ओवरराइट नहीं करते हैं लेकिन एक ईवेंट हैंडलर ('ऑनसेविंग चेंज' या इसी तरह लागू करते हैं, मुझे बिल्कुल याद नहीं है)। – Slauma

7

आप स्लैमा सुझावों के तरीकों का उपयोग कर सकते हैं लेकिन SaveChanges() विधि को ओवरराइड करने के बजाय, आप SavingChanges ईवेंट को अधिक आसान कार्यान्वयन के लिए संभाल सकते हैं।

+0

ओह, अच्छा बिंदु। दरअसल, ऊपर मेरा कोड स्निपेट 'सेविंग चेंज' हैंडलर से आता है, मैं इसे भूल गया। लेकिन यह चीजों को आसान क्यों बनाता है? क्या यह मूल रूप से वही नहीं है जो आपको लॉग लिखने के लिए करना है? – Slauma

+0

यदि आप इसे ओवरराइड संशोधित करने के बजाय इसे जोड़ना चाहते हैं, तो यह आसान हो जाता है, आप केवल एक हैंडलर जोड़ सकते हैं। आपको मूल कार्यक्षमता को पार करने के बारे में चिंता करने की भी आवश्यकता नहीं है, जो मुझे लगता है कि आपके उदाहरण से गुम है। वस्तुओं को वास्तव में कभी भी बचाया नहीं जाएगा। यदि आपने बस एक हैंडलर लिखा है तो आपको इसके बारे में चिंता करने की ज़रूरत नहीं है। – Jay

+0

सच है, धन्यवाद, मैं बेस क्लास के 'सेव चेंज' को कॉल करना भूल गया (अब जोड़ा गया)। (ऐसा इसलिए था क्योंकि मैंने स्निपेट के मुख्य हिस्सों को अपने वास्तविक 'सेविंग चेंज' हैंडलर से कॉपी किया था।) – Slauma

1

ऐसा लगता है कि स्लामा का जवाब जटिल प्रकार की संपत्ति के आंतरिक गुणों में परिवर्तनों का ऑडिट नहीं करता है।

यदि यह आपके लिए कोई समस्या है, तो मेरा उत्तर here सहायक हो सकता है। अगर संपत्ति एक जटिल है और यह बदल गया है तो मैं पूरी जटिल संपत्ति को ऑडिट लॉग रिकॉर्ड में क्रमबद्ध करता हूं। यह सबसे कुशल समाधान नहीं है लेकिन यह बहुत बुरा नहीं है और यह काम पूरा हो जाता है।

+0

ऐसा इसलिए हो सकता है क्योंकि जटिल प्रकारों को प्रॉक्सी नहीं किया जाता है, https://blog.oneunicorn.com/2012/03/13/secrets-of-detectchanges-part-4-binary-properties-and-complex-types/ –

1

मुझे वास्तव में स्लैमा के समाधान पसंद हैं। मैं आमतौर पर संशोधित तालिका का ट्रैक रखना पसंद करता हूं और प्राथमिक कुंजी को रिकॉर्ड करता हूं। यह एक बहुत ही सरल विधि आपको लगता है कि ऐसा करने के लिए उपयोग कर सकते हैं, बुला getEntityKeys(entry)

public static string getEntityKeys(ObjectStateEntry entry) 
    { 
     return string.Join(", ", entry.EntityKey.EntityKeyValues 
           .Select(x => x.Key + "=" + x.Value)); 
    } 
0

देखें Using Entity Framework 4.1 DbContext Change Tracking for Audit Logging है।

DbEntityEntry के साथ।

+1

Whilst यह सैद्धांतिक रूप से प्रश्न का उत्तर दे सकता है, [यह बेहतर होगा] (http://meta.stackexchange.com/q/8259) यहां उत्तर के आवश्यक हिस्सों को शामिल करने के लिए, और संदर्भ के लिए लिंक प्रदान करें। – vonbrand

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