Search code examples
c#databasenhibernateeventslistener

nhibernate auditing with events on update


The following code works on insert but on update modifier is never set, any ideas why?

The code for pre-update is being run and correctly sets the state and entity values to the desired value. However when viewing the generated sql nhibernate does not include the field in the update query.

/// <summary> Updates auditable objects </summary>
public class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
    private ISecurityManager securityManager;

    public bool OnPreInsert( PreInsertEvent args )
    {
        var auditable = args.Entity as IAuditable;
        if (auditable != null) {

            Set( x => auditable.Creator, args.Persister, auditable, args.State, SecurityManager.Identity );
            Set( x => auditable.DateAdded, args.Persister, auditable, args.State, Clock.Now );
        }
        return false;
    }

    public bool OnPreUpdate( PreUpdateEvent args )
    {
        var auditable = args.Entity as IAuditable;
        if (auditable != null) {

            Set( x => auditable.Modifier, args.Persister, auditable, args.State, SecurityManager.Identity );
            //Set( x => auditable.DateModified, args.Persister, auditable, args.State, Clock.Now );
        }
        return false;
    }


    /// <summary> Type safe method to update sate and entity </summary>
    private void Set<T, U>( Expression<Func<U, T>> expression, IEntityPersister persister, U instance, object[] state, T value )
    {
        var member = expression.Body as MemberExpression;
        if (member != null) {

            var index = Array.IndexOf( persister.PropertyNames, member.Member.Name );
            if (index == -1) {
                return;
            }
            state[index] = value;

            var property = (member.Member as PropertyInfo);
            if (property != null) {
                property.SetValue( instance, value, null );
            }
        }
    }

    ISecurityManager SecurityManager
    {
        get { /* From IoC */ }
    }

}

Solution

  • Edit 1: This answer has been improved
    Edit 2: It appears the the real cuase of the problem is dynamic-update set to true as found here however this solution still works for me.

    The changes get saved when you update them in the OnFlushDirty function which is called earlier.

    public override bool OnFlushDirty( object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types )
    {
        bool result = false;
    
        if (entity is IAuditable) {
            var auditable = (IAuditable)entity;
    
            Set( x => auditable.Modifier, propertyNames, auditable, currentState, SecurityManager.Identity );
            //Set( x => auditable.DateModified, args.Persister, auditable, args.State, TwentyClock.Now );
    
            result = true;
        }
    
        return result;
    }