Tuesday, September 15, 2009 9:58 AM srkirkland

Simple auditing using an NHibernate IInterceptor (Part 4)

This is the fourth and final post of a multi-part post series on writing simple auditing functionality for an ASP.NET application using NHibernate.  The requirement was that every object modification event in the system should be logged by username and date.  Specifically I don’t need to know exactly which properties were changed (just that a user was updated by whom at what time), but if you do need to save the changed properties there are plenty of hooks to do that.

I’ll update this post with links to the next parts when they become available

Hooking up the NHibernate IInterceptor

Now that we have written our IInterceptor, implemented as the AuditInterceptor class, we need to tell NHibernate to use it.  This part will vary depending on how NHibernate is exposed to your application, but the basic idea is that when you open a session (using OpenSession() on your NHibernate session factory) you will pass along the interceptor as a parameter.  If you have no interceptor, you can just call OpenSession().

Extending the NHibernate Session Manager

Our session manager class follows loosely with the thread-safe, lazy singleton patterned NHibernateSessionManager described in the NHibernate Best Practices Article.  Adding hooks for a persistent interceptor was pretty easy – first I wrote a method for registering an interceptor:

   1: /// <summary>
   2: /// Allows you to set the interceptor which will be used in all subsequent sessions
   3: /// </summary>
   4: public void RegisterInterceptor(IInterceptor interceptor)
   5: {
   6:     Check.Require(interceptor != null, "interceptor may not be null");
   7:  
   8:     _registeredInterceptor = interceptor;
   9: }

This just sets a local static IInterceptor property called _registeredInterceptor.  This is used whenever opening a session, as follows:

   1: /// <summary>
   2: /// Shortcut which just calls into the GetSession overload with the current registered interceptor.
   3: /// If no interceptor has been registered, it will be null
   4: /// </summary>
   5: public ISession GetSession()
   6: {
   7:     return GetSession(_registeredInterceptor);
   8: }
   9:  
  10: /// <summary>
  11: /// Gets a session with or without an interceptor.  This method is not called directly; instead,
  12: /// it gets invoked from GetSession.
  13: /// </summary>
  14: private ISession GetSession(IInterceptor interceptor)
  15: {
  16:     ISession session = ThreadSession;
  17:  
  18:     if (session == null)
  19:     {
  20:         session = interceptor != null ? sessionFactory.OpenSession(interceptor) : sessionFactory.OpenSession();
  21:  
  22:         ThreadSession = session;
  23:     }
  24:  
  25:     Check.Ensure(session != null, "session was null");
  26:  
  27:     return session;
  28: }

So once the IInterceptor is registered, all future calls to GetSession() will open a session using that interceptor (of course you could null it out again later, though I am not sure why you would want to).

Registering the Interceptor

On Application_Start in the global.asax file I call into the NHibernateSessionManager’s RegisterInterceptor method (seen above).  The interceptor I want to register will be resolved by Castle Windsor through IoC/DI.

   1: //Configure the audit interceptor
   2: NHibernateSessionManager.Instance.RegisterInterceptor(windsorContainer.Resolve<IInterceptor>());

To tell Castle Windsor to use the AuditInterceptor, we add the following line to the IoC container registration:

   1: container.AddComponent("auditInterceptor", typeof (NHibernate.IInterceptor), typeof (AuditInterceptor));
 
And we are done – The interceptor has now been written, tested, and integrated into the NHibernate pipeline.  Now we get to try it out.

Trying It Out

We are going to add a new “project” instance to our database.  Basically in the code ASP.NET MVC creates a new Project instance with a Name property set, and makes sure it is Active by default.  The code is kind of beside the point but I’ll post it anyway just make clear how transparent this interceptor is:

   1: [AcceptPost]
   2: public ActionResult CreateProject(Project newProject)
   3: {
   4:     CreateEntity<Project, int>(newProject, "Project");
   5:  
   6:     return this.RedirectToAction(a => a.Projects());
   7: }
   8:  
   9: private void CreateEntity<T, IdT>(T entity, string type) where T : DomainObject<T, IdT>
  10: {
  11:     var lookupEntity = entity as LookupObject<T, IdT>; // If this is a lookup entity we want to make sure isActive is true
  12:  
  13:     if (lookupEntity != null) lookupEntity.IsActive = true;
  14:  
  15:     ValidationHelper<T>.Validate(entity, ModelState);
  16:  
  17:     if (!ModelState.IsValid)
  18:     {
  19:         return;
  20:     }
  21:  
  22:     //Add the new entity
  23:     Repository.OfType<T>().EnsurePersistent(entity);
  24: }

Let’s see (using NH Profiler) what happened to the database:

Audit

Perfect!  We have two Insert Into statements wrapped in a transaction.  The first statement saves the Project (I didn’t show it, but it is a simple Insert Into Projects with name = “NewProject”), and then second statement saves the audit record.  You can see in the Details tab below that the record shows a Project was created by ‘srkirkland’ and saves the exact time of the action.

An update an delete look pretty much the same – here’s a quick screenshot of an update record being audited:

auditUpdate

Ok – everything looks good.  In the last four posts we have created a fully transparent auditing solution for recording all create/update/delete actions without our application.  Pretty powerful stuff!

Filed under: , , ,

Comments

# Simple auditing using an NHibernate IInterceptor (Part 4) - Scott Kirkland

Wednesday, September 16, 2009 4:12 AM by DotNetShoutout

Thank you for submitting this cool story - Trackback from DotNetShoutout

# re: Simple auditing using an NHibernate IInterceptor (Part 4)

Tuesday, January 05, 2010 10:24 AM by Phone jammer

Great article as for me. It would be great to read more about that theme. The only thing this blog misses is a few pictures of such gadgets as <a href="www.jammer-store.com/.../45_0.jpg">gps jammer</a>.

# re: Simple auditing using an NHibernate IInterceptor (Part 4)

Tuesday, March 29, 2011 4:54 AM by ed

great internet site My goal is to add this website in order to our facebook . as well

# re: Simple auditing using an NHibernate IInterceptor (Part 4)

Tuesday, March 29, 2011 5:18 AM by sildenafil

Do you have any more info on this?

Leave a Comment

(required) 
(required) 
(optional)
(required)