NHibernate Pro Tips

I have used NHibernate quite extensively in the past and here is a quick cumulation of tips to put you on the happy path with NHibernate.

Managing the NHibernate Session in MVC

Ayende has several great blog posts about managing the NHibernate session in an MVC application. Most people open an NHibernate session on the Application Begin Request event and close the NHibernate session on the Application End Request event. In an MVC application you can do better than this by opening on the ActionExecuting event and closing the session on the ActionExecuted event. Ayende's blog post was the first place I saw suggesting it to do this in the MVC ActionExecuting and ActionExecuted events. This prevents N+1 select problems in a view as the Session is closed so you cannot misuse use lazy loading an MVC View. In general use the following solution to open and close your NHibernate session in an ASP .NET application.

public class NHibernateActionFilter : ActionFilterAttribute { private static readonly ISessionFactory sessionFactory = BuildSessionFactory();

  private static ISessionFactory BuildSessionFactory()
  {
      return new Configuration()
          .Configure()
          .BuildSessionFactory();
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
      var sessionController = filterContext.Controller as SessionController;

      if (sessionController == null)
          return;

      sessionController.Session = sessionFactory.OpenSession();
      sessionController.Session.BeginTransaction();
  }

  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
      var sessionController = filterContext.Controller as SessionController;

      if (sessionController == null)
          return;

      using (var session = sessionController.Session)
      {
          if (session == null)
              return;

          if (!session.Transaction.IsActive) 
              return;

          if (filterContext.Exception != null)
              session.Transaction.Rollback();
          else
              session.Transaction.Commit();
      }
      }
  }

public class SessionController : Controller
{
   public HttpSessionStateBase HttpSession
   {
        get { return base.Session; }
   }

   public new ISession Session { get { return NHibernateActionFilter.CurrentSession; } }
  }

Understanding Get vs Load

Get will return the specified entity or null.

Load returns a proxy to the specified entity. If you try to access this proxy then it will load the entity or throw an exception. Use Load to avoid hitting the database when wiring up relationships.

IESI Collections

NHibernate uses the IESI collections by default. So if you want to use ISet for a property on an entity then the ISet implementation must be HashedSet from the IESI Collections. Let's look at example of what you must do out of the box to use ISet with NHibernate.

public class Employee
{
    private ISet<Contact> _contacts = new HashedSet<Contact>();
    public ISet<Contact> Contacts {}
}

Ditching IESI Collections

There are 2 approaches if you want to ditch the usage of the IESI Collections in your domain models.

  1. Use ICollection<T> in place of the IESI ISet<T> and initialize the collection as a System.Collections.Generic.HashSet<T>.

  2. The second approach is to include the Net4Collection class in your project and register this class with NHibernate.

    // Example of nhibernate regisration of Net4Collection class
    configuration.Properties[Environment.CollectionTypeFactoryClass] = typeof(Net4CollectionTypeFactory).AssemblyQualifiedName;
    

NHibernate Semantics

List - Ordered collection of entities, duplicates allowed. Use a .net IList in code. The index column will need to be mapped in NHibernate.

Set - Unordered collection of unique entities, duplicates not allowed. Uses Iesi.Collection.ISet in code. It is important to override GetHashCode and Equals to indicate the business definition of duplicate. Can be sorted by defining a orderby or by defining a comparer resulting in a SortedSet result.

Bag - Unordered list of entities, duplicates allowed. Use a .net IList in code. The index column of the list is not mapped and not honored by NHibernate.

Query Methods in NHibernate

Criteria - Used to build criteria queries. This approach uses strings for property names so it's not as refactor friendly.

MultiCriteria - Used to send multiple queries to the database in a single round trip. A typical use case is to get a page of data and the total page count in one database hit. In general use Future over MultiCriteria.

QueryOver - A strongly typed way of creating a Criteria query.

Linq - Many queries can be written using a standard .net Linq query. You cannot access special capabilities of NHibernate in your query when using a Linq query.

Future<> or FutureValue<> - A Future query is like a lazy MultiCriteria. If you mark 5 queries as Future queries and then access the results of any of those 5 queries it will send a single request to the database. Taking advantage of Future queries can be ideal for loading lots of disparate data on a dashboard landing page in an efficient manner.

Lazy Loading

Many people avoid lazy loading because they have hit N+1 select problems in the past. This is like cutting off your big toe because you have stubbed it in the past. Do not cut off your big toe! In short you need to use lazy loading and understand how to use eager fetching to avoid N+1 select issues.

References