Playing With NHibernate

posted on 08/27/08 at 11:52:42 pm by Joel Ross

Over the past week or so, I've been dabbling with NHibernate. As may be apparent from reading my latest posts over the past few weeks, I've been digging into automated testing more and more, and as I've looked at how I can make the Kinetic Framework more testable, I decided to look elsewhere to see how others are doing it.

NHibernate is probably the most popular ORM tool in the .NET space right now, so it was the obvious place to turn. It's been around for a long time, and it's a tool that's been on my list to learn for a while.

As it turns out, it's actually not that bad to get working. To be honest, I was intimidated by the XML configuration files, but I dove in and eventually figured it out. I started with a very simple object model - the beginnings of a side project I've been thinking about starting. It has four tables - Games, Teams, Weeks, and Seasons. The intent of these tables is to be able to build a schedule for the NFL. You'll see the data I'm storing as we explore the objects further, but I do have a few relatively complicated relationships here. Seasons have a collection of games (by week), and Games are made up of a Season, a Week, a Home Team and an Away Team.

The models themselves are pretty much POCOs. For example, here's my Team class:

   1:  public class Team
   2:  {
   3:    public virtual int TeamId { get; set; }
   4:    public virtual string Abbreviation { get; set; }
   5:    public virtual string Location { get; set; }
   6:    public virtual string NickName { get; set; }
   7:  }

Like I said - there isn't much there. The virtual keyword is required by NHibernate (as is a default constructor - I didn't include it, but it's implied).

The part I was most worried about was the mapping files. In reality, the mapping file is more complicated than the object itself, but it's not that bad. Here's the Team object mapping XML:

   1:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="CP.Models" namespace="CP.Models">
   2:    <class name="CP.Models.Team" table="Teams">
   3:      <id name="TeamId" column="TeamId" type="Int32" unsaved-value="0">
   4:        <generator class="native" />
   5:      </id>
   6:      <property name="Abbreviation" column="Abbreviation" type="string" length="50" not-null="false"/>
   7:      <property name="Location" column="Location" type="string" length="50" not-null="false" />
   8:      <property name="NickName" column="NickName" type="string" length="50" not-null="false" />
   9:    </class>
  10:  </hibernate-mapping>

There's a few interesting things here. First, the Id element. This defines how a Team can be identified from other teams. In my database, it's an auto-number int, and I want to allow my database to manage the numbering. First, I map my identifying property to TeamId, which maps to TeamId in the database. The generator specifies how NHibernate should figure out what a new Id should be. "Native" tells NHibernate to pick the best method for getting the id. In this case, it'll let the database handle it. The other property elements are pretty straightforward - just how each property on my Team class is mapped to the database, it's type, whether it can be null, and it's length.

The interesting (and intimidating) part of the mapping files is more evident when we look at the Game class.

   1:  public class Game
   2:  {
   3:    public virtual int GameId { get; set; }
   4:    public virtual Week Week { get; set; }
   5:    public virtual Season Season { get; set; }
   6:    public virtual Team HomeTeam { get; set; }
   7:    public virtual Team AwayTeam { get; set; }
   8:  }

The game class shows it's relationships. How those manifest themselves is evident through its mapping file, which is a bit more complicated.

   1:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="CP.Models" namespace="CP.Models">
   2:    <class name="CP.Models.Game" table="Games">
   3:      <id name="GameId" column="GameId" type="Int32" unsaved-value="0">
   4:        <generator class="native" />
   5:      </id>
   6:      <many-to-one name="Week" column="WeekId" not-null="true" 
   7:        fetch="join" outer-join="false" cascade="save-update" />
   8:      <many-to-one name="HomeTeam" column="HomeTeamId" not-null="true" 
   9:        cascade="save-update" fetch="join" outer-join="false"  />
  10:      <many-to-one name="AwayTeam" column="AwayTeamId" not-null="true" 
  11:        cascade="save-update" fetch="join" outer-join="false" />
  12:      <many-to-one name="Season" column="SeasonId" not-null="true" 
  13:        cascade="save-update" fetch="join" outer-join="false" />
  14:    </class>
  15:  </hibernate-mapping>

The Id element is the same as above. The difference is that instead of properties, this object has other objects as part of it - the many-to-one elements. Each element maps to a property on the Game class, and specifies which column in the Games table to get the Id for the related object. The rest specify how it should get the data (I think - I got this working, and didn't look at it too much further yet).

Once I created all of the classes and created the mapping files (and the hibernate config file, which I won't show), it's just a matter of starting up the NHibernate configuration. At some point, I saw the idea of a helper class that you can use to get new NHibernate sessions, so that's what I went with.

   1:  public class NHibernateHelper
   2:  {
   3:    private static ISessionFactory _sessionFactory;
   4:    private static ISessionFactory SessionFactory
   5:    {
   6:      get
   7:      {
   8:        if (_sessionFactory == null)
   9:        {
  10:          var configuration = new Configuration().Configure();
  11:          configuration.AddAssembly(Assembly.GetExecutingAssembly());
  12:          _sessionFactory = configuration.BuildSessionFactory();
  13:        }
  14:        return _sessionFactory;
  15:      }
  16:    }
  17:   
  18:    public static ISession OpenSession()
  19:    {
  20:      return SessionFactory.OpenSession();
  21:    }
  22:  }

The first time this code gets called, it sets up NHibernate, and tells it to look for configurations in the executing assembly (which contains both the mapping files as embedded resources and the models themselves). Once done, I can just call NHibernateHelper.OpenSession() and get a new session for working with my objects.

Once that was done, I started building repositories to talk to NHibernate to do the retrieval. I'm new to the Repository pattern, but based on a bit of research and a few samples, I came up with a base interface:

   1:  public interface IRepository<T> where T : new()
   2:  {
   3:    T Create();
   4:    T GetById(int id);
   5:    void Save(T model);
   6:    void Delete(T model);
   7:    ICollection<T> GetList();
   8:  }

I think, as I build this out more, I'll add more methods (or at least overloads to existing ones) that allow me to run queries, but for each object, I'll have a concrete implementation of the IRepository<T> interface for it. But the implementation for a lot of these methods will look very similar and have to deal with the details of sessions in NHibernate. For each of my repositories, I didn't want them to have to inherit from a base class that implements the interface, so I came up with a set of extension methods that handle the interface methods. Here's an example of the Delete() extension method:

   1:  internal static void Delete<T>(this IRepository<T> repository, T model) where T : new()
   2:  {
   3:    using (ISession session = NHibernateHelper.OpenSession())
   4:    {
   5:      using (ITransaction transaction = session.BeginTransaction())
   6:      {
   7:        session.Delete(model);
   8:        transaction.Commit();
   9:      }
  10:    }
  11:  }

This doesn't actually satisfy the interface requirements (I'm not sure an extension method even can do that), so in my repositories, I still have to make a call. Here's the call for delete in the TeamRepository (which implements IRepository<Team>):

   1:  public void Delete(Team team)
   2:  {
   3:    this.Delete<Team>(team);
   4:  }

Now, I can create new repositories quickly, and my physical implementation doesn't have to worry about the NHibernate details. Interestingly (and only in theory, since I haven't tried it), I could switch out extension methods, and switch which ORM tools I am using, since the only references to NHibernate is in the extension methods and the NHibernateHelper class (which, in turn, is only used by the extension methods).

Once this is all done, retrieving data is pretty simple. My sample application is written using the ASP.NET MVC framework, and my TeamController has a List method that's just this simple:

   1:  public ActionResult List()
   2:  {
   3:    return View(Repository.GetList());
   4:  }

That's it! I can get the data I need, without ever writing any data access code.

It's tough to compare this to the Kinetic Framework, as I still have some learning to do. The sheer amount of code I have to deal with is considerably less with NHibernate, but the majority of the code in the Kinetic Framework is generated, and not really something I have to maintain. This method does lend itself to being able to write automated tests for it, since I can mock the repositories easily, which is not easily accomplished in the Kinetic Framework (although a Repository pattern can be placed on top of the Kinetic Framework to do this).

I'll post more about my experimentations, but so far, I like NHibernate a lot. I'm still learning the ropes, and would like to know how the query is working - for example, when I get a game, does it retrieve the home team, away team, week, and season all in one query, or does it lazy load the properties as needed? I'll find that out, but I just haven't had the time yet.

Regardless, it's nice to use a solid framework and not have to worry about the ugly details underneath (or at least not have to be the one to maintain the ugly details!)

Categories: Development, Software, C#