NHibernate 2.0 Alpha1 was released a few days ago and the 2.0 version brings a lot of new features. There is a completly new event / listener system that can be used for all sorts of cross-cutting concerns. A basic example would be to register a save/update listener that would update some common fields (like ModifiedBy, ModifiedDate).

In order to use the event system you need to create a class that either inherits from a default nhibernate event listener or from the many event interfaces. Here is an example:

using NHibernate.Event;
using NHibernate.Event.Default;

public class CustomSaveEventListener : DefaultSaveEventListener
{
    protected override object PerformSaveOrUpdate(SaveOrUpdateEvent evt)
    {
        IEntity entity = evt.Entity as IEntity;
        if (entity != null)
            ProcessEntityBeforeInsert(entity);

        return base.PerformSaveOrUpdate(evt);
    } 

    internal virtual void ProcessEntityBeforeInsert(IEntity entity)
    {
        User user = (User) Thread.CurrentPrincipal;
        entity.CreatedBy = user.UserName;
        entity.ModifiedBy = user.UserName;
        entity.CreatedDate = DateTime.Now;
        entity.ModifiedDate = DateTime.Now;
    }
}

The reason I inherit from the default implementation is that when you specify a listener in the configuration you will replace the default listener. I am not sure what the recommended way to this is but the way I understand it is that you can choose two ways, iether you inherit a default implementation and only specify your listener in the configuration or you just implement the event interface(ISaveOrUpdateEventListener for example) but then you also need to specify the default implementation in the configuration (in order not to loose functionality).

Here is how the configuration looks like:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
        ...
        <listener class="NHibTest.Listeners.CustomSaveEventListener, NHibTest" type="save" />
        <listener class="NHibTest.Listeners.CustomSaveEventListener, NHibTest" type="save-update" />                    
    </session-factory>
</hibernate-configuration>

You can also place the listeners within a event xml element if you want to setup many listeners of the same type. It is also possible to do this via code like this:

Configuration cfg = new Configuration();
cfg.EventListeners.SaveEventListeners = 
    new ISaveOrUpdateEventListener[] {new CustomSaveEventListener() };

There are many other event types, for example Load, PreUpdate, DirtyCheck, Autoflush, PostDelete. These events could be used in interesting scenarios, for example validation and security.

11 comments:

Anonymous said...

Hi

I tried to work this out with Nhibernate 2.0 but it still doesnt have the namespace mentioned Nhibernate.Event am I missing something?

Torkel Ödegaard said...

Are you sure you downloaded the NHibernate 2.0 Alpha1 release?

It should be there (It is for me) :)

pdu said...

Nice stuff but it seems to modify all object before saving it to database.

I think we need to test if the object isDirty (or something else) ....

Do you have any ideas? ???

santo2 said...

hi, thanks for your code.
Now the only thing i'm experiencing is that when i have a nested category structure (i have a parent Id), and i want to save one of my objects that has another item in it's collection of child members, it repeats twice (for the parent and the child), it's not saved to the database, and it's not giving any errors. I wanted to inspect the PerformSaveOrUpdate method but there is nothing in it, it's just a virtual method.

Greets

Anonymous said...

How can hook this listenere with castle ActiveRecord ?.

Anonymous said...

hi,

nice work!
i have to implement a similar system and want to store a ModifyDate/ModifyUser in my Entities. For my Aggregate Root Entity (XObject) the ISaveOrUpdateEventListener is called and i can update the values the same way as your sample. Problem is, i also want to update the values ModifyUser/Date of a dependent Entity (XObjectVersion) and the Event is not called for the dependant entity.

I also tried to hook in the event 'IPreInsertEventListener' - this is called for both, my Aggregate root entity and my dependant entity BUT when i set values to the entity fields, they are not included in the SQL Statement that is sent to the database.

Has anybody any ideas how to solve this?
Maybe this is a bug because the sql statement is not sent to the database at this state of the process but changes to entity fields are not recogniced.

Torkel Ödegaard said...

Good question, I am not sure what could cause that. I suggest you post a question on the nhibernate forum or nhibernate users mailing list.

Gerke Geurts said...

When hooking the SaveOrUpdate event it is important to also hook the Save and Update events as well, as these will be called for child entities depending on whether the child entities are new or not.

Anonymous said...

How to create new objects within this event?

I'm trying to save my audit log in different table, so I must create new object and save it but i doesn't work.

New objects are not persisted. :/

James Yoo said...

Most of the questions posted here are already answered on Ayende's blog here: http://ayende.com/Blog/archive/2009/04/29/nhibernate-ipreupdateeventlistener-amp-ipreinserteventlistener.aspx

George said...

Has anyone been able to implement an 'isDirty' check in the event listener.

It seems to be modify the audit prorperties for all entities in the transaction, as opposed to only those ones that have been modified.