Tuesday, April 12, 2011

Using the NHibernate Repository Pattern in C# ASP .NET

Introduction

A large variety of ORM object relational mapping tools exist for use with developing C# ASP .NET web applications. Some of the more popular ORM tools include LINQ to SQL, the Entity Framework, and NHibernate. While previous articles have discussed developing web applications with LINQ to SQL and the Entity Framework through the usage of the business object generator, Linquify, in this tutorial, we'll focus on implementing the repository pattern for an NHibernate web application.

Repository Pattern vs Business Objects

Before discussing the details of implementing the repository pattern with NHibernate, it's important to note the difference between the repository model of data storage versus the business object model. 
The repository model consists of an individual class which accepts and returns type objects, including NHibernate DTO types, to store and retrieve in the database. The repository itself contains all methods and code for working directly with NHibernate.

The business object model provides database storage and retrieval methods directly on each NHibernate type, usually via inheritance. A brief example highlights the core differences as shown below. 
The Repository Model

The NHibernate ORM repository model provides a repository class, which handles all method calls for saving and loading NHibernate types in the database. An example of calling the repository class appears as follows:

Repository repository = new Repository();
Person person = new Person();
person.Name = "John Doe";
repository.Save(person);

Notice in the above code, we've instantiated a Person class, populated its values, and then passed the person to the repository for saving. In this case, the Person class is a strict DTO data transfer object and contains no data access methods. All NHibernate types in this scenario would be passed to the repository to execute NHibernate database methods.

The Business Object Model

The NHibernate business object model provides methods on each NHibernate type for saving and loading in the database. An example of calling the NHibernate methods on a business object type appears as follows:

Person person = new Person();
person.Name = "John Doe";
person.Save();

Notice in the above code, we've instantiated a Person class, just as we did with the repository model. However, since each NHibernate type contains the necessary NHibernate method calls, we no longer need a repository class. We can directly call NHibernate methods from each type, such as person.Save(), person.Delete(), person.Find(), etc. In this model, each NHibernate type can inherit from a business class base type, which provides the required methods.

Taking the Repository One Step Further

One drawback to the Business Object model is the lack of sharing support for database connections (NHibernate session) and transactions. Since each NHibernate type in the business object model has their own copy of NHibernate methods, it may be more cumbersome to share the NHibernate session and transaction between types. We can resolve this with the repository model, allowing us to utilize a transaction enabled repository pattern as shown in the following example.

A Transaction Enabled Repository Pattern

 using (RepositoryBase repository = new RepositoryBase())
 {
     try
     {
         repository.BeginTransaction();
         Person person = (Person)repository.GetById(typeof(Person), 12);
         person.Age += 1;
         
         repository.Save(person);
     }
     catch
     {
         repository.RollbackTransaction();
    }
 }

Notice in the above code, we've instantiated an NHibernate repository pattern within a "using" clause. This allows us to automatically dispose of the repository once we've completed our processing. As you'll see below, our repository's Dispose() method, flushes the NHibernate session, commits transactions, closes the session, and handles cleaning. As you can tell, the repository pattern really speeds up the integration of NHibernate's methods with our C# ASP .NET web applications. The above code is using an NHibernate transaction via the repository pattern, but using a transaction is optional. You could leave out the transaction commands, in which case NHibernate's standard session will be used. Let's move on to implementing the NHibernate Repository Pattern in C#.

Read more: Primary Objects