Recently I decided to change the data access layer in my blog from Entity Framework to NHibernate. In this post I'll tell you Why and How I did it.
The WHY
I still have some issues with the current version of the Entity Framework as I posted in the article MVC Storefront: Migrating to the Entity Framework. Many of the things I wanted to do with the EF like using POCOs or LazyLoading will be possible in version 4. Until then I decided to give NHibernate a shot. I figured it wouldn't be so hard since I already had some knowledge with Hibernate from my Java days.
The HOW
The migration was not that hard. NHibernate has a great documentation with good examples. I also used the NHibernate in Action book which I highly recommend. It is only 400 pages and very to the point.
How the Project is Structured
I have a Website and 4 assemblies:
SpeakOut.Data: This is where the DAOs live, both their interface and implementation. Also in this assembly are the NHibernate mapping files.
SpeakOut.Model: This assembly has all the domain model entities. I had to make minor changes to these classes to get them to work best with NHibernate. The collections for instance were changed to use IList<T> instead of the IEnumerable<T> I was using with the EF.
SpeakOut.Service: This assembly has services (Interfaces and Implementations) that are used by the presentation layer.
SpeakOut.Lib: Here are all the utility classes, base classes for the pages and control and classes with extension methods.
The Model
This is the class diagram for the classes in the model. NHibernate makes populating the dependencies show in the diagram really easy, actually you don't do anything and you still get the benefit of lazy loading for free. This is something that wasn't easy in the EF.

The Mapping Files
Here are the mapping files for my entities.
Author
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="SpeakOut.Model"
namespace="SpeakOut.Model">
<class name="Author">
<id name="AuthorId">
<generator class="guid"/>
</id>
<property name="Name"/>
<property name="Login"/>
<property name="Password"/>
<property name="Email"/>
<property name="IsAdmin"/>
<bag name="Posts" access="field.camelcase-underscore" inverse="true" cascade="all-delete-orphan">
<key column="AuthorId"/>
<one-to-many class="Post"/>
</bag>
</class>
</hibernate-mapping>
Post
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="SpeakOut.Model"
namespace="SpeakOut.Model">
<class name="Post">
<id name="PostId">
<generator class="guid"/>
</id>
<property name="Title"/>
<property name="Body"/>
<property name="Permalink"/>
<property name="Published"/>
<property name="PublishDate"/>
<bag name="Comments" access="field.camelcase-underscore" inverse="true" cascade="all-delete-orphan">
<key column="PostId"/>
<one-to-many class="Comment"/>
</bag>
<bag name="Tags" access="field.camelcase-underscore" inverse="true" cascade="all-delete-orphan">
<key column="PostId"/>
<one-to-many class="Tag"/>
</bag>
<bag name="Categories" access="field.camelcase-underscore" table="CategoryPost">
<key>
<column name="PostId" not-null="true"/>
</key>
<many-to-many class="Category">
<column name="CategoryId" not-null="true"/>
</many-to-many>
</bag>
<many-to-one name="Author" column="AuthorId" not-null="true"/>
</class>
</hibernate-mapping>
Comment
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="SpeakOut.Model"
namespace="SpeakOut.Model">
<class name="SpeakOut.Model.Comment">
<id name="CommentId">
<generator class="guid"/>
</id>
<property name="Author"/>
<property name="Body"/>
<property name="Email"/>
<property name="Website"/>
<property name="Country"/>
<property name="Ip"/>
<property name="IsApproved"/>
<property name="CreatedAt"/>
<many-to-one name="Post" column="PostId" not-null="true"/>
</class>
</hibernate-mapping>
Tag
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="SpeakOut.Model"
namespace="SpeakOut.Model">
<class name="Tag">
<id name="TagId">
<generator class="guid"/>
</id>
<property name="Name"/>
<many-to-one name="Post" column="PostId" not-null="true"/>
</class>
</hibernate-mapping>
Category
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="SpeakOut.Model"
namespace="SpeakOut.Model">
<class name="Category">
<id name="CategoryId">
<generator class="guid"/>
</id>
<property name="Name"/>
<bag name="Posts" table="CategoryPost">
<key>
<column name="CategoryId" not-null="true"/>
</key>
<many-to-many class="Post">
<column name="PostId" not-null="true"/>
</many-to-many>
</bag>
</class>
</hibernate-mapping>
The Data Access
In the data access layer I decided to drop the Repository I was using to go with simple DAOs. The main reason I did it was to test NHibernate as recommended by it's more experienced users. I really think I wouldn't get a lot from the Repository.
Here is the class diagram for the DAOs interfaces:

One of the approaches suggested in the NHibernate in Action book is to use a base DAO that encapsulate the common behavior in all NHibernate DAOs. Here is the code for the this class:
namespace SpeakOut.Data.NHibernate.Daos
{
public class BaseDao<T> : IBaseDao<T>
{
private ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = NHibernateHelper.GetCurrentSession();
}
return _session;
}
}
public T FindById(Guid id)
{
return Session.Load<T>(id);
}
public T FindByIdAndLock(Guid id)
{
return Session.Load<T>(id, LockMode.Upgrade);
}
public IList<T> FindAll()
{
return Session.CreateCriteria(typeof (T)).List<T>();
}
public T MakePersistent(T entity)
{
Session.SaveOrUpdate(entity);
return entity;
}
public void MakeTransient(T entity)
{
Session.Delete(entity);
}
}
}
All the DAOs derive from this BaseDao and get all the basic functionality. The specialized DAOs are left to implement the functionality that is directly related to them. Look at how simple the implementation of the AuthorDao is:
namespace SpeakOut.Data.NHibernate.Daos
{
public class AuthorDao : BaseDao<Author>, IAuthorDao
{
public Author FindByLogin(string login)
{
return Session.CreateCriteria<Author>()
.Add(Expression.Eq("Login", login).IgnoreCase())
.UniqueResult<Author>();
}
}
}
One aspect to pay attention to is how the NHibernate Session works when you have an ASP.NET application. The easiest way to deal with this is to create an HTTP Module that creates a session that will live as long as the duration of the Request. When the Request ends the Transaction is committed and the Session is released. A similar strategy is described in this post.
Here is the implementation of the HTTP Module.
public class NHibernateCurrentSessionWebModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += Application_BeginRequest;
context.EndRequest += Application_EndRequest;
}
public void Dispose()
{
}
private void Application_BeginRequest(object sender, EventArgs e)
{
ISession session = NHibernateHelper.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
private void Application_EndRequest(object sender, EventArgs e)
{
ISession session = CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory);
if (session == null) return;
try
{
session.Transaction.Commit();
}
catch (Exception)
{
session.Transaction.Rollback();
}
finally
{
session.Close();
}
}
}
Since this module creates the Session and the Transaction we need a way to access these resources. This is done by a helper class like this:
namespace SpeakOut.Data.NHibernate
{
public static class NHibernateHelper
{
public static readonly ISessionFactory SessionFactory;
static NHibernateHelper()
{
try
{
Configuration configuration = new Configuration();
configuration.AddAssembly("SpeakOut.Data");
SessionFactory = configuration.Configure().BuildSessionFactory();
}
catch (Exception ex)
{
throw new Exception("NHibernate initialization failed", ex);
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
public static ISession GetCurrentSession()
{
return SessionFactory.GetCurrentSession();
}
}
}
There are two important things about this class. The first is AddAssembly method being called in the static constructor. I have to pass the name of the assembly that contains the NHibernate Mappings. In this project all the mappings are in the Speakout.Data assembly. The second important point is the GetCurrentSession which is the method that will be used in all the DAOs to get the Session already created in the HTTP Module.
More info about configuring NHibernate when using several assemblies can be found here.
The Configuration
Since I'm using a WebSite all the NHibernate configuration is done in the Web.Config. First a section is defined in the ConfigSections node:
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
And the create the configuration node:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">
Server=(local);initial catalog=your_catalog;user id=your_user;password=your_pass
</property>
<property name="adonet.batch_size">10</property>
<property name="show_sql">true</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="use_outer_join">true</property>
<property name="command_timeout">60</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
<property name="current_session_context_class">web</property >
</session-factory>
</hibernate-configuration>
Final Words
I hope my implementation may help you. It simple and small enough that you can go through it very quickly and get a base understanding of what you need to get NHibernate going.
I'll probably give the Entity Framework another chance once my hosting starts to support EF 4. After all changing and trying, and failing and learning is exactly what this blog is all about.
You can get all the source code for this project at Codeplex.