views:

769

answers:

3

I'm revisiting and re-implementing the code that caused me to ask this question about data auditing in NHibernate. However this time, I want go with Sean Carpenter's suggestion and implement the ISaveOrUpdateEventListener (new in NHibernate 2.x)

I want to add a row in a database for each change to each property with the old value and the new so later on in the UI I can say stuff like "User Bob changed Wibble's Property from A to B on 9th March 2009 at 21:04"

What's the best way to compare the object state to work out which of the object's properties have changed?

You can get the loaded state of the object by the following:

    public void OnSaveOrUpdate(SaveOrUpdateEvent saveOrUpdateEvent)
    {
       object[] foo = saveOrUpdateEvent.Entry.LoadedState;
    }

And I suppose I could use reflection to work out which properties have changed, but I've been digging around and there doesn't seem to be a matching set of properties to compare it too. I would've thought there would a GetChangedProperties() method or something.

I could always get the old object from the database as it is and compare it, but that's yet another database hit and would seem heavy handed in this scenario.

What's the best direction to take with this?

P.S. In case it makes any difference, this is an ASP.NET-MVC / S#arp Architecture project.

+1  A: 

You could implement a Memento pattern to serialize the state from the initial Load, via ILoadEventListener. Obviously you don't want to store collections / relationships or you'd duplicate the whole object graph. You can then pull this out and compare in your ISaveOrUpdateEventListener.

James L
+2  A: 

I would not know how to achieve what you want with the ISaveOrUpdateListener interface - you could however take advantage of the fact that IPreUpdateEventListener and IPreInsertEventListener interfaces both provide what you need... e.g. do something like this:

public bool OnPreUpdate(PreUpdateEvent evt)
{
    string[] names = evt.Persister.PropertyNames;
    for(int index = 0; index < names.Length; index++)
    {
        // compare evt.State[index] to evt.OldState[index] and get
        // the name of the changed property from 'names'
        // (...)
    }
}

and do the same thing for pre insert.

mookid8000
This worked for me. Thanks mookid.
IainMH
+3  A: 

You could also use the FindDirty method on the Persister to let NHibernate do the comparisons for you:

var dirtyFieldIndexes = @event.Persister.FindDirty(@event.State, @event.OldState, @event.Entity, @event.Session);

foreach (var dirtyFieldIndex in dirtyFieldIndexes)
{
 var oldValue = @event.OldState[dirtyFieldIndex];
 var newValue = @event.State[dirtyFieldIndex];

 // Log values to new "AuditLog" object and save appropriately.
}
Darrell Mozingo
all kinds of awesome!
Andrew Bullock