Monday, January 17, 2011

The NHibernate Anti Pattern

On Stack Overflow A lot of NHibernate questions come with a code snippet similar to the following

publlc IList<MyObject> GetObjects()
{
   using (ISession session = sessionFactory.OpenSession())
   {
      return session.CreateCriteria<MyObject>().List<MyObject>();
   }
}

What's wrong with this code? Well at first glance not much. We're opening a session doing our query and then cleaning up any open resources.

Well if we were dealing with ADO.NET, opening a connection, doing our work, then closing the connection is a typical process. However, NHibernate sessions are a lot more than database connections. One of the biggest benefits is the first level cache. The session caches objects that have been retrieved by their id as well as saved objects. This means that if you've retrieved an object once, as long as you haven't closed the session, any time you retrieve the object again, NHibernate will get you the cached version. This saves a round trip to the database.

Another benefit is batching database writes. Since updates are stored in the cache, NHibernate doesn't need to write them to the database until it needs to. If you let NHibernate flush automaticially it can save its updates until it needs to and then run them all in one round trip to the database.

What about lazy loading? If our MyObject class looks like this:
public class MyObject
{
   public int Id { get; set; }
   public AnotherObject Reference { get; set; }
}
and we wanted to access the Reference field we would need to eagerly load each associated AnotherObject. This is because NHibernate uses proxy objects. When your references are lazy loaded and you access the Reference field, NHibernate will use the same session the original object was loaded with to fetch the associated reference. If the session has been closed you will receive an Exception telling you that the associated session has been closed.

So if the pattern in the first code snippet is a bad one, which pattern should we be following? The folks over at NHibernate recommend the Unit of Work pattern. The idea is that you create a session and keep it alive for the duration of a so called "conversation". An example of a conversation could be the HttpRequest in a web application. So a session would be opened at the beginning of the request and at the end of the request would be closed and disposed. You can even wrap the session in a transaction for extra reliability.

On a windows app it's a little more tricky. One way to define a unit of work is in the context of a thread. Another might be to handle a user action, For example if a user action triggers a button to update some data, a new session would be created. Then closed after all work has been completed.

Finally you can use your favorite IoC container to define how and when a new session is created and then your data access layer can be injected with the reusable session.

No comments:

Post a Comment