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.
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 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:
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 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:
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.
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.
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#.
To begin the repository pattern, we’ll start with an interface. By providing an interface for the repository pattern to implement, we setup the core framework for the repository design pattern, which can be utilized by a business class layer, along with test driven development or other types of repositories.
Our repository interface is fairly simple. It contains four basic methods for saving, deleting, loading, and listing NHibernate entities.
Before implementing the IRepository interface, we’ll need a utility class to help manage the NHibernate SessionFactory. Since the SessionFactory can utilize a degree of resources, we’ll want to make the manager class static, allowing our repository to utilize the session as required, without having to recreate it each time.
The above Database class is a generic NHibernate SessionFactory manager, allowing us access to the session by calling the OpenSession() method. The method calls the NHibernate OpenSession() method on the static SessionFactory object, returning a valid NHibernate session. Note, the “NhibernateTest.Types” is the name of your DTO types assembly where you’ve defined the NHibernate types and XML files (*.hbm.xml).
The repository class is where the NHibernate method handling is provided. NHibernate DTO types will be passed into this class for data storage or retrieval. We implement the class, based upon the repository interface, as follows:
The first item to note is that the repository implements our IRepository interface, which provides the basic Save, Load, Delete, List methods. It also implements the IDisposable interface, which is key to allowing us to use the “using” clause with our repository for easy clean up and maintenance of NHibernate database connections.
We utilize two private members in our repository class. One member for managing the NHibernate session and another member for managing an NHibernate transaction. The constructor of our class initializes the NHibernate session. You can create a new NHibernate session by using the empty constructor or pass in an existing session to re-use the same NHibernate context and connection.
To utilize transactions with our repository class, we provide the BeginTransaction(), CommitTransaction(), and RollbackTransaction() methods. Note, with the default implementation above, CommitTransaction() will be called automatically in the Dispose() method. For those who wish to cancel a transaction by default, rather than committing, you can remove the call to CommitTransaction() in the Dispose() method, as commented in the code.
The remainder of the functions for saving, loading, listing, etc call the standard NHibernate methods along with the NHibernate DTO types. The methods are designed to be generic, accepting any variety of DTO objects. Note, the ToList() method returns an IQueryable interface, in order to provide LINQ style manipulation of data prior to SQL query execution, so the NHibernate LINQ reference will be needed. In addition, you could add query functions to the repository.
The final item of note in the NHibernate Repository Pattern is the Dispose() method. This method first checks if a transaction is being used, and if so, commits the transaction by default. It then flushes the session, committing any remaining queries, closes the session, and disposes of the session and remaining objects. By utilizing the repository pattern within a “using” clause, this happens automatically.
With the repository pattern defined, we can utilize it as shown below.
The NHibernate repository can also be used with a shared session, across multiple classes in the C# ASP .NET web application as follows:
Since we’ve defined a repository interface, we have the flexibility to create multiple repositories, such as a test repository to work with test driven development. Note, for simplicity in the article, we’ve focused strictly on the NHibernate repository pattern, but we’ve left out an important logical piece of the repository pattern, which is the implementation of a business layer that would receive the concrete repository type and return objects from. The business layer would accept an IRepository type, allowing us to pass any implementation of the repository. For more details on implementing the repository pattern, view Implementing the Repository Pattern.
In the above code for a test repository, we’ve implemented the IRepository interface, and provided bodies for the Save, Delete, Load, and List methods. Save and Delete simply assume a successful result. Load instantiates the passed-in object and returns it, simulating a load from the database. ToList functions in a similar manner and instantiates a single passed-in object type and adds it to the list of entities to be returned, simulating a List from the database.
We can then use the two repositories as follows:
The above example would save to the actual database via NHibernate. We can also implement the test repository to simulate saving to the database (for unit testing logic without affecting the database), as follows:
The only required change in these two examples is the instantiation of RepositoryBase() or RepositoryTest(). You can find more detail about implementing multiple repository patterns and advanced techniques of implementing the concrete classes via reflection in the article Implementing the Repository Pattern.
Since we’ve discussed the NHibernate business object model and compared it to the repository pattern, it would only be fair to provide an implementation as well. The business object model allows calling each of the NHibernate method functions directly on the DTO objects. We can implement a start of the business object model as follows:
Note in the above code, we’ve defined a C# generic business base class, which all NHibernate DTO classes would inherit from, providing the data access methods to each type. The methods of the business base class are also defined as generic in order to work with any DTO type. While sessions and transactions are not sharable between classes by the client, this business class could be extended to provide this functionality.
To utilize the business object model, you would define your NHibernate DTO types to inherit from the base class as follows:
NHibernate provides a complete set of data access methods for working with database functionality in C# ASP .NET web applications. The flexibility of the repository pattern, when used with NHibernate, provides a clean data access layer for integration in applications, with the ability to support transaction and database session management. By implementing a repository pattern or business object model in conjunction with NHibernate DTO types, developers can help create more robust and maintainable web applications.
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.