sharp bites

standing on the shoulders of giants

AltNerDinner: Part 4. Introducing NHibernate. Because POCO is enough

It’s time already to introduce a proper persistence solution. NHibernate can be a little intimidating at first for a new-comer, but I undoubtedly think it’s the way to go. And it’s not really more difficult than the other contenders anymore. Its two major drawbacks (hand writing all that XMhelL mapping and not strongly typed queries) are solved (or in serious process of it) by Fluent NHibernate and NHibernate.Linq.

Other than that, the only difficulty relies in learning a few concepts inherent to OR/Ms. Most notably the Unit of Work pattern and Lazy Loading, and all the stuff around them (namely repositories, query objects, persistence ignorance, POCOs, DTOs, entities/value objects and equality, inheritance mapping, dynamic proxies, caching, anemic domains, active record, transactions, concurrency, session lifetime, unit testing, select n+1 problems and other pitfalls to avoid,  …). [NOTE: If you need more info on this topics I’d recommend you to visit the official nhibernate site.]

Sounds scary? It might seem using an OR/M brings in more problems that the ones it solves, but I don’t think it is the case, except for maybe the most trivial apps (like AltNerdDinner, hehe, I know). Even if you are writing a relatively simple app, you can still greatly benefit from using NHibernate (or Active Record on top of it). If your app has inherent complexity, well, of course you’ll have to do your homework and learn a few concepts, but that’s not your OR/M’s fault. Using a hand-rolled DAL won’t make your problems go away (quite the opposite, I’d dare to say).

As I said, most (if not all) of this problems exist in all OR/M, it’s just that some of them somehow try to hide this complexity in one or another way. The problem is, this is a can of worms that can lead to very bad practices and slap on your face at any moment. And this is were I think this is were NHibernate excels at, its flexibility. It gets out of your way.

Ok, enough talking! Show me the code!

To use NHibernate and NH.Linq we need to add a reference in our project to the following dll’s: NHibernate, NHibernate.Linq, FluentNHibernate (if we hadn’t already) and lastly NHibernate.ByteCode.Castle if we want to use Castle’s dynamic proxy as our proxy generator of choice.

Here is the code for my DinnerRepository implementation using NHibernate:

public class NhDinnerRepository : IDinnerRepository
{
private readonly ISession _session;

public NhDinnerRepository(ISession session)
{
_session = session;
}

private INHibernateQueryable<Dinner> GetDbContext()
{
return _session.Linq<Dinner>();
}

public IQueryable<Dinner> FindAllDinners()
{
return GetDbContext().AsQueryable();
}

public IQueryable<Dinner> FindByLocation(float latitude, float longitude)
{
return GetDbContext().Where(d => d.Distance(latitude, longitude) < 100).AsQueryable();
}

public IQueryable<Dinner> FindUpcomingDinners()
{
return from dinner in GetDbContext()
where dinner.EventDate > DateTime.Now
orderby dinner.EventDate
select dinner;
}

public Dinner GetDinner(int id)
{
return GetDbContext().SingleOrDefault(d => d.DinnerID == id);
}

public void Save(Dinner dinner)
{
if (!dinner.IsValid)
{
throw new ApplicationException("Rule violations");
}

_session.SaveOrUpdate(dinner);
}

public void Delete(Dinner dinner)
{
_session.Delete(dinner);
}


As you see, I am returning IQueryable<Dinner> instead of traditional .NET collections. Some people think IQueryable is the best thing since slice bread, others are ditching repositories entirely, some prefer using query objects, others advocate for explicit repositories, some use generic ones and I just don’t know yet, but decided to go at least with repositories because I want to take control of how entities are persisted.



To instantiate my NHibernate repository, I need to supply it with an ISession. I don’t create the ISession inside the repository, since this is considered a bad practice. Instead, I create a new Session per request. To do that, I expected I would have some infrastructure in place in MvcContrib, but that was not the case. Jeffrey Palermo posted a NHibernate wrapper to manage ISession, but I thought that class had too many responsibilities, so I separated it in the following.



public class SessionFactoryBuilder
{
private static ISessionFactory _sessionFactory;

private readonly IPersistenceConfigurer _dbConfiguration;

public SessionFactoryBuilder(IPersistenceConfigurer dbConfiguration)
{
_dbConfiguration = dbConfiguration;
}

public ISessionFactory Build()
{
if (_sessionFactory == null)
{
_sessionFactory = GetDbConfiguration().BuildSessionFactory();
}

return _sessionFactory;
}

public Configuration GetDbConfiguration()
{
return Fluently.Configure()
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Dinner>())
.Database(_dbConfiguration)
.BuildConfiguration();
}
}


This builds the session factory and also returns the configuration (useful to create the schema). It needs an IPersistenceConfigurer, which I supply from the constructor, in order to be able to switch between different configurations (i.e. MSSQL and SQLite), as shown below:



public class MsSqlPersistenceConfigurerFactory : IPersistenceConfigurerFactory
{
private string _connectionString;

public MsSqlPersistenceConfigurerFactory(string connectionString)
{
_connectionString = connectionString;
}

public IPersistenceConfigurer GetPersistenceConfigurer()
{
return MsSqlConfiguration.MsSql2005
.ConnectionString(c => c.Is(_connectionString))
.ShowSql()
.FormatSql()
.ProxyFactoryFactory<ProxyFactoryFactory>();
}
}


Life of a Session



We need to create the aforementioned ISession somehow. In Web applications, it’s generally a good practice to create a new Session on every request. For that, Ayende recently suggested to just stick it in the global.asax, but I prefer to put it in a separate  IHttpModule.



public class NhSessionPerRequestModule : IHttpModule
{
private readonly ISessionFactory _sessionFactory;

public NhSessionPerRequestModule(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
public void Init(HttpApplication application)
{
application.BeginRequest += delegate
{
CurrentSession = _sessionFactory.OpenSession();
};

application.EndRequest += delegate
{
if (CurrentSession != null)
{
CurrentSession.Dispose();
}
};
}

public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
private set { HttpContext.Current.Items["current.session"] = value; }
}

public void Dispose()
{
}
}



In my Global.asax, I configure it as follows:



private static readonly ISessionFactory SessionFactory = CreateSessionFactory();
private static IWindsorContainer _container;

private static ISessionFactory CreateSessionFactory()
{
string connString = ConfigurationManager.ConnectionStrings["AltNerdDinner"].ConnectionString;
return new SessionFactoryBuilder(
new MsSqlPersistenceConfigurerFactory(connString)
.GetPersistenceConfigurer())
.Build();
}

private readonly NhSessionLifetimeModule _nhSessionLifetimeModule =
new NhSessionLifetimeModule(SessionFactory);

public override void Init()
{
base.Init();
_nhSessionLifetimeModule.Init(this);
}


Lastly, this is my current configuration of the container in order to register the components:



private void RegisterComponents()
{
_container = new WindsorContainer();
_container.AddFacility<FactorySupportFacility>();

ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(_container));

_container.Register(
Component.For<ISession>()
.UsingFactoryMethod(() => NhSessionLifetimeModule.CurrentSession)
.LifeStyle.Transient);

_container.Register(
Component.For<IDinnerRepository>()
.ImplementedBy<NhDinnerRepository>().LifeStyle.Transient);

_container.RegisterControllers(Assembly.GetExecutingAssembly());
}

Comments