Daniel Little
Daniel Little

Reputation: 17263

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 */ }
    }

}

Upvotes: 5

Views: 1896

Answers (2)

StevieTimes
StevieTimes

Reputation: 465

For us it was because NHibernate was joining tables together with the same column names; so we'd update the first instance of that column name, but not the subsequent ones. We did this:

    protected void Set(IEntityPersister persister, object[] state, string propertyName, object value)
    {
        var index = Array.IndexOf(persister.PropertyNames, propertyName);
        while (index > -1)
        {
            state[index] = value;
            index = Array.IndexOf(persister.PropertyNames, propertyName, index + 1);
        }
    }

Upvotes: 0

Daniel Little
Daniel Little

Reputation: 17263

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

Upvotes: 3

Related Questions