views:

732

answers:

1

I decided to implement the event listeners in the latest build of NHibernate to keep track of who is making edits and what those edits are. My question is this - the below does work and I can step through it but what I'm not sure how these changes get saved ... do I need to build an audit table and write a mapping for it to call a save or what method is best to get the state of the object along w/ the "updated by" and "updated date" information so I can bring this up for someone at a later date.

I thought the base class provided this (or a subset of this functionality) but I can't seem to find a good blog post on what I'm missing here. Any help would be much appreciated!

Imports NHibernate.Event
Imports NHibernate.Event.Default

Public Class CustomSaveEventListener
    Inherits DefaultSaveEventListener

    Protected Overloads Overrides Function PerformSaveOrUpdate(ByVal evt As SaveOrUpdateEvent) As Object
        Dim entity As IEntity = TryCast(evt.Entity, IEntity)
        If entity IsNot Nothing Then
            ProcessEntityBeforeInsert(entity)
        End If

        Return MyBase.PerformSaveOrUpdate(evt)
    End Function

    Friend Overridable Sub ProcessEntityBeforeInsert(ByVal entity As IEntity)
        Dim user As User = DirectCast(Thread.CurrentPrincipal, User)
        entity.ModifiedBy = user.UserName
        entity.ModifiedDate = DateTime.Now
    End Sub
End Class

When I open Reflector I see the below for this base class method - but what is it doing exactly?

protected override object PerformSaveOrUpdate(SaveOrUpdateEvent @event)
{
    EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);
    if ((entry != null) && (entry.Status != Status.Deleted))
    {
        return this.EntityIsPersistent(@event);
    }
    return this.EntityIsTransient(@event);
}
+2  A: 

When an entity is made persitent (saved or loaded), NH adds it to an internal dictionary (in session.PersistenceContext). The key of this dictionary is an EntityEntry, which contains some information about the entity state (and some other stuff i guess). If the entity is not saved (transient), it won't find an EntityKey in the internal dictionary.

By reading it, it looks like this function looks it the entity is transient or not, and calls the good function (I guess EntityIsTransient triggers save behaviour, EntityIsPersitent triggers update behaviour).

 EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);
    if ((entry != null) && (entry.Status != Status.Deleted))
    {
        return this.EntityIsPersistent(@event);
    }
    return this.EntityIsTransient(@event);

But about your problem, it looks like a log system. Why don't you just implement ISaveOrUpdateEvent and use log4net to log edits with it?

Nelson
Thanks for the detail about Session.PersistenceContext - about the logging I'm actually trying to keep the "history" behavior of a system in place but instead of using a stored procedure to do insert a history on save, I wanted NHibernate to do this for me. Is the only way to persist something like this to create a mapping file for a history table in SQL server and call the update in this class? (just after the save or update event)
Toran Billups
You're welcome. I cannot answer with details to second question. I don't feel good about your idea. When you intercept the save or update event, NH is already in a database access and batching process. I try not to modify NH behaviour once it started flushing, by fear of breaking something. A NH-only solution I see would be for you to stack all changes, and, after session is flushed, redo a save of your log class, or at the end of your program execution.
Nelson
If you got an exception in you nh session (like connection loss), that makes things even more complex. That is why I talked about log4net (http://logging.apache.org/log4net/index.html). You can specify you want it to write your log to a database or something else (look for appenders). log4net configuration is a bit hard to understand at the beginning, but I bet this is the tool you need.
Nelson
I like the solution you hinted at w/ log4net. One question about this approach - is it best to seralize the object and store it or should I try to capture each property of the object and log it w/ the other details (time/user)?
Toran Billups
Capture properties. If you serialize your object you'll need to create an application to unserialize it. I guess you don't want to do it if you wanted originaly to use NH to store it to a database!For other questions about log4net, you should reask the community or look for documentation, I'm a total noob with log4net.
Nelson
Maybe this question can be marked as answered?
Nelson