Monday, September 14, 2009

Intercepting NHibernate

One of the greatest things about NHibernate ,that I like so much, is that you can customize it very easily.
Most of the times you can work with POCO but if you want to do some custom stuffs when an object is going to be worked upon via NHibernate you can implement NHibernate.ILifeCycle (NHibernate.Classic.ILifeCycle in NHibernate 2) interface that has some OnXXX methods that let you add your functionality when something is going to happen.Using this mechanism you can customize the persistence behavior of individual objects but what if you have a pattern that applies to large amount of objects in your application?

To make it clear let's consider this scenario :
You want to add a feature to a set of implemented objects to keep who has created them and who changed them for the last time.
Since there might be so many places where these common information should be set in the code it could be a hard task to change the code.Here's where NHibernate.IInterceptor comes to play.IInterceptor provides some methods that let you intercept session actions.
Enough talking let's write some code for above scenario:

First I create an IUserActivity interface :

public interface IUserActivity
{
User CreatedBy{get;set}
User LastUpdatedBy{get;set;}
}



Now all the objects that want to save their user activity should implement this interface (and these properties should be defined in database and in mapping files)

Then I create an Interceptor that implements NHibernate.IInterceptor :
(Since I'm going to change the state of objects before saving/updating them I only need to implement OnSave method and to notify NHibernate that other methods are not implemented simply we return the method default value of its return type.)

public class NHInterceptor:IInterceptor
{
.....

public void OnSave(object entity,object id,object[] state,
string
[] propertyNames,IType[] types )
{
var userActivity=entity as IUserActivity;

if(userActivity==null) return;
//I use an IoC container to get the AuthenticationProvider that
//provides user information.

var authenticationProvider=
ObjectRepository.Resolve<AutenticationProvider>();
if(userActivity.CreatedBy==null) // this object is not created yet

{
userActivity.CreatedBy=authenticationProvider.CurrentUser;
}
userActivity.LastUpdatedBy=authenticationProvider.CurrentUser;
}
.....
}


As you can see you can easily change the state of your objects before saving them using an interceptor.

Other arguments of OnSave methods hold new information for entity.State array has the values ,propertyNames array has the name of properties and types array has types of properties.

To intercept a session simply open a session providing an interceptor:

var session=SessionFactory.OpenSession(new NHInterceptor());

No comments: