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!

13 Comments

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

  • Do you have any more info on this?

  • Can you please point me to an example where, along with the audit information, the properties that have been changed are saved? Thanks.

  • Burberry Bags cushion stiff and sore as well as problem legs, molding for the legs as well as currently being for that reason very good all of the time. This Burberry Bags is usually attractive to check -- Just think itrrrs great -- speculate I reside in South Florida, the actual consumption shall be small.

  • Hey there, You have done an excellent job. I will definitely digg it and personally recommend to my friends. I'm confident they'll be benefited from this website.

  • Hey, you used to write great, but the last several posts have been kinda boring I miss your tremendous writings. Past several posts are just a little bit out of track! come on

  • Hurrah, that's what I was seeking for, what a material! existing here at this weblog, thanks admin of this website.

  • Simply put i went along to to make an effort these particular diablo 3 gold regarding when i lost control in love. I used relating to the proverb coloured styles where they used to be basically remarkable. Initial I did used in my little actual size yet they used to be slightly major. Thus used regarding these particular diablo 3 gold one size lesser where they used to be a great fit in! Simply put i travelled round inside them a handful of where they used to be quite fluffy together with fine. I quickly travelled onto one to view the way that they seemed regarding myself where they seemed good. Thus invested in all of them when i are not able to delay until I'm able to get all of them simply because feel happy and check for that reason attractive.

  • Great blog right here! Also your website rather a lot up very fast! What web host are you using? Can I get your affiliate link in your host? I desire my website loaded up as fast as yours lol

  • In fact, cancer treatment was back in California, where he began working
    for Bluebird Productions. When helium prices go up it will make it easier to discover which of the Cancer Treatment gun safes are UL
    rated for both security and fire. As a result of Kate's showing off, in the beginning, the two most common types of handgun are the semi-automatic pistol and the double-action revolver.

  • I am really delighted to read this webpage posts which contains plenty of helpful information, thanks for providing these information.

  • hello.thanks for your posted,i really love your site,thanks

  • hello.thanks for your posted,i really love your site,thanks

Comments have been disabled for this content.