Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data Auditing in NHibernate using events

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. 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.

like image 803
Iain Holder Avatar asked Mar 09 '09 16:03

Iain Holder


2 Answers

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.
}
like image 84
Darrell Mozingo Avatar answered Nov 08 '22 14:11

Darrell Mozingo


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.

like image 28
mookid8000 Avatar answered Nov 08 '22 13:11

mookid8000