Making NHibernate Inversion of Control-Friendly

UPDATE: Fabio Maulo, the man behind NHibernate, posted the following recently on the topic:

http://fabiomaulo.blogspot.com/2008/11/entities-behavior-injection.html

-----

NHibernate is a little bit of a pill when it comes to dependency injection. Since it has its roots in the Java world it implements many helpful patterns such as Unit of Work, among others. Even so, the original Hibernate framework predates the time when dependency injection was widely used, so it does not adequately facilitate the use of this technique.

There are a number of way that you can get NHibernate to work with your IoC container. Initially we thought about using a service locator-style approach (the original IoC approach) but there are a number of reasons why a service locator is inferior to dependency injection.

We had several requirements when implementing our solution:

  1. Use dependency injection
  2. Allow NHibernate's deferred query execution magic through proxy objects

Those two requirements might initially seem to be at odds with each other. Even as we fully implemented our solution as found below you will see that we were only able to reach a satisfactory compromise between the two.

In our data access class (e.g. our repository) we have the following private member fields which are provided to the repository through dependency injection:

private Type concreteType;
private Action initialize;
private ISession session;

The "concreteType" object will tell NHibernate the specific concrete class to construct. The "T" argument tells us the interface implemented by the concrete type (IContact -> DefaultContact, IAddress -> DefaultAddress, IUser -> DefaultUser, etc.) ISession is NHibernate's session interface.

The "initialize" object services as a kind of callback that fills or populates the object (via setter injection) after it is created. We use setter injection rather than constructor injection because we want to maintain the benefits of having NHibernate's proxy object in order to utilize deferred execution of queries.

If it weren't for NHibernate's deferred execution the entire methodology outlined herein would be different. Specifically we would working with a a factory rather than funky callbacks.

Putting It All Together

public virtual T FindById(int id)
{
var loaded = (T)this.session.Load(this.concreteType, id);
this.initialize(loaded);
return loaded;
}

In the above method we ask NHibernate's session to retrieve the appropriate object from the database (or its cache). We then provide that object to the "initialize" callback. You can map the callback to whatever code you'd like. In our case we map it back to our factory object which then injects, via setter injection, any dependency that needs to be set. Because the factory deals with concrete classes (rather than interfaces) and because it is allowed to have a knowledge of the internal workings of the class it creates, it is the ideal place to perform the "initialize" callback.

After the initialize callback is complete the object that was loaded is fully injected and ready to be used. With this solution we avoid any of the complicated internal workings of NHibernate and we are better able to control the construction (meaning initialization) of the objects that are retrieved from NHibernate.