I’m a huge fan of NHibernate. It has excellent documentation and just makes the whole job of getting things into and out of the database much more enjoyable. There is a whole series of posts on NHibernate from one of the committers, Ayende. When Fluent NHibernate (FNH) came out, it was like butter on sliced bread. FNH makes it even easier to use NHibernate.
Ayende had a great post a while back on how to use Event Listeners. What does an Event Listener do, and furthermore, what does it look like? Listeners give you really low level access to an object just before it gets saved into the database. That allows you at this point to get to both the old state and the new state of an object. You can at this point do any auditing you need to do on the insert, update or delete. I believe you could even block the changes if necessary, but I would probably do that at a higher level before reaching this point. One of the best uses for listeners is the one presented below, where you can modify the fields for an insert date, a modified date, and the modifying user.
Event Listeners
namespace projectname.infrastructure.app.auditing
{
using System;
using System.Security.Principal;
using System.Web;
using domain;
using NHibernate.Event;
using NHibernate.Persister.Entity;
public class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
public string get_identity()
{
return WindowsIdentity.GetCurrent().Name;
}
public bool OnPreInsert(PreInsertEvent event_item)
{
Auditable audit = event_item.Entity as Auditable;
if (audit == null)
{
return false;
}
DateTime? entered_date = DateTime.Now;
DateTime? modified_date = DateTime.Now;
string identity_of_updater = get_identity();
store(event_item.Persister, event_item.State, "entered_date", entered_date);
store(event_item.Persister, event_item.State, "modified_date", modified_date);
store(event_item.Persister, event_item.State, "updating_user", identity_of_updater);
audit.entered_date = entered_date;
audit.modified_date = modified_date;
audit.updating_user = identity_of_updater;
return false;
}
public bool OnPreUpdate(PreUpdateEvent event_item)
{
Auditable audit = event_item.Entity as Auditable;
if (audit == null)
{
return false;
}
DateTime? modified_date = DateTime.Now;
string identity_of_updater = get_identity();
store(event_item.Persister, event_item.State, "modified_date", modified_date);
store(event_item.Persister, event_item.State, "updating_user", identity_of_updater);
audit.modified_date = modified_date;
audit.updating_user = identity_of_updater;
//insert auditing object here
return false;
}
public void store(IEntityPersister persister, object[] state, string property_name, object value)
{
int index = Array.IndexOf(persister.PropertyNames, property_name);
if (index == -1)
{
return;
}
state[index] = value;
}
}
}
Notice I had to update each value twice. Once on the item and once in the new state. I believe (and I could be wrong) that we update the state so it gets saved to the database and we update the object to match the state of the database so we are in sync. It's a small price to pay for getting this low level access.
Great! Now I have my listeners, but if I run it now, this never gets called. That’s because I haven’t told NHibernate about these event listeners. So I go searching some on the interwebs to learn how to register the listeners. And I come across this. Ayende talks about how to registration in the configuration file in a later post, but with FNH, I register in code. Hmmm… back to the interwebs…
Then I came across Adam Aldrich’s post on how to register the listeners in code. This is from his post on registration:
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
cfg.EventListeners.PreUpdateEventListeners =
new IPreUpdateEventListener[] { new AuditEventListener() };
cfg.EventListeners.PreInsertEventListeners =
new IPreInsertEventListener[] { new AuditEventListener() };
_sessionFactory = cfg.BuildSessionFactory();
This is just what I was looking for! Code-based registration. But how do I use FNH to set that up? That’s where some nice detective work and a fluent interface come in.
Fluent NHibernate Registration of Event Listeners
private void build_factory()
{
if (nhibernate_session_factory == null)
{
nhibernate_session_factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.ConnectionString(c =>
c.FromConnectionStringWithKey("db")))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<VideoMapping>())
.ExposeConfiguration(cfg => {
cfg.SetListener(ListenerType.PreInsert,
new AuditEventListener());
cfg.SetListener(ListenerType.PreUpdate,
new AuditEventListener());
})
.BuildSessionFactory();
}
}
What FNH has given me here is a way back to the configuration. Now I have full access to do anything I need to do with NHibernate. With it’s discoverability, FNH makes it a joy to use and makes NHibernate even more awesome!
Dear reader, what libraries do you find a joy to use? And why?
Posted
11-20-2009 10:38 PM
by
Rob Reynolds