2010-06-22 12 views
8

आयुेंड के पास ईवेंट हैंडलर का उपयोग करके NHibernate (here) के लिए एक सरल ऑडिट ट्रेल को कार्यान्वित करने के बारे में एक लेख है।एनएचबीर्नेट ऑडिट ट्रेल जो "फ्लश द्वारा संग्रह संसाधित नहीं किया गया" त्रुटियों का कारण नहीं बनता है

दुर्भाग्य से, के रूप में टिप्पणी में देखा जा सकता है, उसके कार्यान्वयन का कारण बनता है निम्नलिखित अपवाद उत्पन्न होने के लिए: संग्रह xxx फ्लश()

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

मैंने कामकाजी कार्यान्वयन के निर्माण के लिए मेरी सबसे कठिन कोशिश की है लेकिन बिना किसी किस्मत के।

क्या किसी को भी एक समाधान समाधान के बारे में पता है?

+0

बस स्पष्ट करने के लिए, मुझे एक ऐसा समाधान चाहिए जिसमें आलसी लोडिंग या अन्य महत्वपूर्ण कार्यक्षमता बलिदान शामिल न हो। मैं एक ऐसा समाधान भी चाहता हूं जो डेवलपर-सुरक्षित है - अत्यधिक अस्पष्ट कारण के लिए अपवाद को बहुत ही अलग किया जा सकता है, और अक्सर प्रतिलिपि या डीबग करना लगभग असंभव होता है। – cbp

उत्तर

4

यह बिल्कुल आसान नहीं है। मैंने ऐसा कुछ लिखा है, लेकिन यह हमारी जरूरतों के लिए बहुत विशिष्ट है और तुच्छ नहीं है।

कुछ अतिरिक्त संकेत:

अगर संदर्भों का उपयोग

NHibernateUtil.IsInitialized(entity) 

या

NHibernateUtil.IsPropertyInitialized(entity, propertyName) 

आप IPersistentCollection करने के लिए संग्रह डाल सकता लोड किए गए हैं परीक्षण कर सकते हैं। मैं कार्यान्वित एक IInterceptor मैं कहाँ मिलता है प्रत्येक संपत्ति के NHibernate प्रकार, मैं नहीं जानता कि जहां यह जब घटनाओं का उपयोग कर सकते हैं:

if (nhtype.IsCollectionType) 
{ 
    var collection = previousValue as NHibernate.Collection.IPersistentCollection; 
    if (collection != null) 
    { 
     // just skip uninitialized collections 
     if (!collection.WasInitialized) 
     { 
      // skip 
     } 
     else 
     { 
      // read collections previous values 
      previousValue = collection.StoredSnapshot; 
     } 
    } 
} 

जब आप NHibernate से अद्यतन घटना मिलता है, उदाहरण के आरंभ नहीं हो जाता। आप सुरक्षित रूप से आदिम प्रकारों के गुणों तक पहुंच सकते हैं। जब आप ToString का उपयोग करना चाहते हैं, तो सुनिश्चित करें कि आपका ToString कार्यान्वयन किसी भी संदर्भित संस्थाओं और न ही किसी संग्रह को एक्सेस नहीं करता है।

आप एनबीबेर्नेट मेटा-डेटा का उपयोग यह पता लगाने के लिए कर सकते हैं कि किसी प्रकार को किसी इकाई के रूप में मैप किया गया है या नहीं। यह आपके ऑब्जेक्ट मॉडल में नेविगेट करने के लिए उपयोगी हो सकता है। जब आप किसी अन्य इकाई का संदर्भ देते हैं, तो इसे बदलते समय आपको अतिरिक्त अपडेट ईवेंट मिलेंगे।

+0

आप '@ event.Persister.PropertyTypes [propertyIndex] का उपयोग करके ईवेंट पर्सिस्टर से टाइप प्राप्त कर सकते हैं। ICollectionType' और '@ event.Persister.PropertyTypes [propertyIndex] IsAssociationType'। इन तक पहुंच के साथ भी, मुझे अभी भी पता नहीं लगा है कि संबंधित इकाई प्रकारों का ऑडिट कैसे करें। –

9

मैं निम्नलिखित तरीके को उपयोग करते हुए एक ही समस्या का समाधान करने में सक्षम था:

public void OnPostUpdate(PostUpdateEvent postEvent) 
{ 
    if (IsAuditable(postEvent.Entity)) 
    { 
     //skip application specific code 

     foreach (var collection in postEvent.Session.PersistenceContext.CollectionEntries.Values) 
     { 
      var collectionEntry = collection as CollectionEntry; 
      collectionEntry.IsProcessed = true; 
     } 

     //var session = postEvent.Session.GetSession(EntityMode.Poco); 
     //session.Save(auditTrailEntry); 
     //session.Flush(); 
    } 
} 

आशा इस मदद करता है वर्तमान हठ संदर्भ में सभी संग्रह पर सच को संसाधित ध्वज सेट श्रोता के भीतर।

+6

क्या आपको कोई विचार है कि 'संभावित रूप से अवांछित) साइड इफेक्ट्स को' IsProcessed 'को' true 'में सेट किया जा सकता है? –

8

फिक्स निम्न होना चाहिए। NHibernate.Event.Default.DefaultFlushEventListener से किसी नई घटना श्रोता वर्ग बना सकते हैं और निकाले जाते हैं यह:

cfg.EventListeners.FlushEventListeners = new IFlushEventListener[] { new FixedDefaultFlushEventListener() }; 

आप हाइबरनेट JIRA में इस बग के बारे में अधिक पढ़ सकते हैं::

[Serializable] 
public class FixedDefaultFlushEventListener: DefaultFlushEventListener 
{ 
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

    protected override void PerformExecutions(IEventSource session) 
    { 
     if (log.IsDebugEnabled) 
     { 
      log.Debug("executing flush"); 
     } 
     try 
     { 
      session.ConnectionManager.FlushBeginning(); 
      session.PersistenceContext.Flushing = true; 
      session.ActionQueue.PrepareActions(); 
      session.ActionQueue.ExecuteActions(); 
     } 
     catch (HibernateException exception) 
     { 
      if (log.IsErrorEnabled) 
      { 
       log.Error("Could not synchronize database state with session", exception); 
      } 
      throw; 
     } 
     finally 
     { 
      session.PersistenceContext.Flushing = false; 
      session.ConnectionManager.FlushEnding(); 
     } 

    } 
} 

NHibernate configuraiton दौरान यह रजिस्टर https://hibernate.onjira.com/browse/HHH-2763

एनएचबर्ननेट की अगली रिलीज में यह भी शामिल होना चाहिए।

+0

इसने मुझे बहुत समय बचा लिया है! धन्यवाद ! – Jacob

+1

बस टिप्पणी में संदर्भ जोड़ने के लिए: "अगली रिलीज में उस फिक्स को शामिल करना चाहिए"। 3.3.3 जीजी के रूप में यह विलय नहीं लग रहा है, इसलिए यह अभी भी उपयोगी है। – Jafin

+0

क्या यह फिक्स DefaultAutoFlushEventListener के लिए भी मान्य है? ऐसा लगता है कि दोनों श्रोताओं को समस्या से समर्थन मिलता है, मुझे यकीन नहीं था कि एक ही कोड दोनों का उपयोग किया जाना चाहिए। – user1838662

0

मैं यह निर्धारित करने में सक्षम था कि यह त्रुटि तब फेंक दी जाती है जब एप्लिकेशन कोड एक आलसी प्रस्ताव लोड करता है जहां इकाई का संग्रह होता है।

मेरा पहला प्रयास नए CollectionEntries (जिसे मैं कभी भी संसाधित नहीं करना चाहता क्योंकि वास्तव में कोई बदलाव नहीं होना चाहिए) के लिए देख रहा था। फिर उन्हें IsProcessed = true के रूप में चिह्नित करें ताकि वे समस्याएं नहीं पैदा कर सकें।

var collections = args.Session.PersistenceContext.CollectionEntries; 
var collectionKeys = args.Session.PersistenceContext.CollectionEntries.Keys; 
var roundCollectionKeys = collectionKeys.Cast<object>().ToList(); 
var collectionValuesClount = collectionKeys.Count; 

// Application code that that loads a Lazy propery where the Entity has a collection 

var postCollectionKeys = collectionKeys.Cast<object>().ToList(); 
var newLength = postCollectionKeys.Count; 

if (newLength != collectionValuesClount) { 

    foreach (var newKey in postCollectionKeys.Except(roundCollectionKeys)) { 
     var collectionEntry = (CollectionEntry)collections[newKey]; 
     collectionEntry.IsProcessed = true; 
    } 
} 

हालांकि यह इस मुद्दे को पूरी तरह से हल नहीं करता था। कुछ मामलों में मुझे अभी भी अपवाद मिल जाएगा।

जब OnPostUpdateCollectionEntries शब्दकोश में मानों कहा जाता है सब पहले से ही IsProcessed = true सेट किया जाना चाहिए। तो मैंने यह देखने के लिए एक अतिरिक्त जांच करने का फैसला किया कि संग्रह की संसाधित न होने की अपेक्षा की गई है या नहीं।

var valuesNotProcessed = collections.Values.Cast<CollectionEntry>().Where(x => !x.IsProcessed).ToList(); 

if (valuesNotProcessed.Any()) { 
    // Assert: valuesNotProcessed.Count() == (newLength - collectionValuesClount) 
} 

जिन मामलों में मेरा पहला प्रयास तय किया गया है, इन नंबरों का बिल्कुल मिलान होगा। हालांकि जिन मामलों में यह काम नहीं करता है, वहां शब्दकोश में अतिरिक्त आइटम अलरे थे। मेरे में यह सुनिश्चित हो सकता है कि इन अतिरिक्त वस्तुओं के परिणाम भी अपडेट नहीं होंगे, इसलिए मैं को सभी valuesNotProcessed के लिए सेट कर सकता हूं।

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