Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An NHibernate audit trail that doesn't cause "collection was not processed by flush" errors

Tags:

nhibernate

Ayende has an article about how to implement a simple audit trail for NHibernate (here) using event handlers.

Unfortunately, as can be seen in the comments, his implementation causes the following exception to be thrown: collection xxx was not processed by flush()

The problem appears to be the implicit call to ToString on the dirty properties, which can cause trouble if the dirty property is also a mapped entity.

I have tried my hardest to build a working implementation but with no luck.

Does anyone know of a working solution?

like image 730
cbp Avatar asked Jun 22 '10 06:06

cbp


2 Answers

I was able to solve the same problem using following workaround: set the processed flag to true on all collections in the current persistence context within the listener

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

Hope this helps.

like image 115
Yura Ageev Avatar answered Sep 29 '22 12:09

Yura Ageev


The fix should be the following. Create a new event listener class and derive it from NHibernate.Event.Default.DefaultFlushEventListener:

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

    }
} 

Register it during NHibernate configuraiton:

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

You can read more about this bug in Hibernate JIRA: https://hibernate.onjira.com/browse/HHH-2763

The next release of NHibernate should include that fix either.

like image 27
desunit Avatar answered Sep 29 '22 11:09

desunit