From Entity Framework to NHibernate

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.

MVC Storefront: Migrating to the Entity Framework

The title of the post is actually misleading. A better title (although too long) would be: Migrating the Repository concept used in the MVC Storefront to the Entity Framework.

Lately I’ve been playing more with the Entity Framework. The other day I was trying to replicate the way Rob Conery used the Repository Pattern in the MVC Storefront sample application and I was surprised to learn that the Entity Framework has some differences from LINQ to SQL that I did not expect.

My first test was very simple. I wanted to get an IQueryable of Posts with it’s respective Comments attached. The StoreFront did this in a really clever and simple manner. The following code was supposed to work:

class BlogRepository
{
    SOEntities ctx = new SOEntities();

    public IQueryable<Comment> GetComments()
    {
        return from comment in ctx.Comments.Include("Post")
               select new Comment()
               {
                   CommentId = comment.CommentId,
                   Author = comment.Author,
                   Body = comment.Body,
                   PostId = comment.Post.PostId
               };
    }

    public IQueryable<Post> GetPosts()
    {
        var posts = from post in ctx.Posts
                    let comments = GetComments()
                    select new Post()
                           {
                               PostId = post.PostId,
                               Title = post.Title,
                               Body = post.Body,
                               Comments = comments.Where(x => x.PostId == post.PostId)
                           };
        return posts;
    }
}

class Post
{
    public Guid PostId { get; set; }

    public string Title { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }
}

class Comment
{
    public Guid CommentId { get; set; }

    public Guid PostId { get; set; }

    public string Author { get; set; }

    public string Body { get; set; }

}

class Program 
{
static void Main(string[] args)
{
    BlogRepository rep = new BlogRepository();
    var posts = rep.GetPosts();
    posts = posts.Where(x => x.Title.Contains("MVC"));
    List<Post> postList = posts.ToList();
}
}

I get an error saying that Linq to Entities does not recognize the method GetComments().

It’s not a bug, it’s is just the way the EF works. The method GetComments can’t be translated to anything in the provider. But why does it work with Linq to Sql you ask? Because it converts the Linq expression in a different way. Sorry but the post would be too long if I got into this subject.

The way recommended by the EF team is you use the AsEnumerable() extension method, forcing the initial query to be executed and then you would be working with objects. Here is how I needed to change my code for this to work:

public IQueryable<Post> GetPosts()
{
    var posts = from post in ctx.Posts.AsEnumerable()
                let comments = GetComments()
                select new Post()
                       {
                           PostId = post.PostId,
                           Title = post.Title,
                           Body = post.Body,
                           Comments = comments.Where(x => x.PostId == post.PostId)
                       };
    return posts.AsQueryable();
}

This does work, but when ToList() method is called in the posts object, instead of filtering the posts in the sql code, all the posts are brought from the database and then they are filtered as objects. This is a work around but it’s not ideal.

I have another work around, it also isn’t the ideal but it does work in the sense that it will apply the filter in the database.

public IQueryable<Post> GetPosts()
{
    var comments = GetComments();
    var posts = from post in ctx.Posts
                select new Post()
                       {
                           PostId = post.PostId,
                           Title = post.Title,
                           Body = post.Body,
                           CommentsQry = comments.Where(x => x.PostId == post.PostId)
                       };
    return posts;
}

class Post
{
    public Guid PostId { get; set; }

    public string Title { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }

    public IQueryable<Comment> CommentsQry
    {
        set { Comments = new LazyList<Comment>(value); }
    }
}

The trick is that I introduced a IQueryable property in the Post class. This property receives an IQueryable and under the hood converts it to a LazyList and sets the Comments property. Doing this overcomes the limitation that the EF has in not letting you use constructors that take parameters in the EF code. It only allows parameterless constructors on the select projection.

If you run this code you will see that the records are filtered in the database. The reason I still don’t consider this solution ideal is that I had to make a change to my model class and created a property that is not related to the business but rather to a limitation imposed by the framework. I still don’t see a way around this. If anyone has any suggestions please do leave them as comments.

A very interesting post was written by Muhammad Mosa a while ago. It’s worth visiting his blog to checkout his idea.