Easy Database Access with NHibernate in .NET 5.0
By: Manfred Lange
Tags: C Sharp Programming, Orm, Nunit, Fluent Nhibernate, Nhibernate

Adding Persistence with NHibernate

To add persistence, we need three elements:

The first element is adding and configuring NHibernate

The second element is changing the TodoListRepository class to use NHibernate to store and read objects.

The third element we need a mapping between the TodoList class and the underlying relational table

NHibernate, Fluent NHibernate and Entity Framework

There are several options for object-relational mappers (ORMs) for .NET. These choices include NHibernate (“NH”) and Entity Framework (“EF”). Without going into much details beyond mentioning briefly discussing the two main reasons, we will be using NH in this article.

The first reason is that there appear more articles about EF than NH. Also, many examples and the tooling in Visual Studio give EF the preference. This is not surprising as EF is pushed heavily by Microsoft. This doesn’t mean, though, that it should be the default, let alone default choice. It is not a sacrilege to replace Microsoft’s option with an option developed and maintained by a large open-source community. This article (and more in the future) aim at making NH more accessible by showing how easily it can be introduced.

The second reason is that in my work I observe an increasing number of clients who run into significant issues with EF, many of which could have been avoided by using something other than EF. There are different types of issues but most of them appear gradually as a project grows in size. Yes, all of the problems can be solved. However, at times that is very time consuming and the problems have a tendency to reappear. Please note, though, that my opinion is that if EF works for you, then don’t change it.

In this article, we’ll be using NHibernate. To make code even simpler to write, we’ll be using a nuget package named Fluent NHibernate. This library makes configuring NHibernate and writing mapping code much easier. More about this shortly. Let’s get started.

Adding and Configuring NHibernate

Go to directory “/app/CmdLine” and run the following command to add Fluent NHibernate to the project:

dotnet add package FluentNHibernate

Although FluentNHibernate includes NHibernate as an indirect reference as well, this may not be the most recent stable version. To ensure it is, run the following command to add a direct reference:

dotnet add package NHibernate

Switch back to directory “/app” and run “dotnet test” to confirm we didn’t break anything.

Open the file containing class Database. Right after method “CreateServices()” add the following method “CreateSessionFactory()”:

In this piece of code in line 9 we run the migrations. Then in lines 11 to 18 we configure NHibernate using the fluent interface provided by Fluent NHibernate. In lines, 12 to 14 we provide the information about which SQL dialect and connection string to use. Lines 15 to 17 configure the mappings to be used (more on that later). Line 16 describes where to look for mapping classes. In line 17 we tell Fluent NHibernate to look for column names with suffix “Id” to identify foreign keys. For example, let’s assume we already had a table for the items in a TodoList. In that case that table would have a column “TodoListId” to store the information about which to-do list the item belongs to. Finally, in line 18 we build the session factory (more on this shortly).

With this in place, we can update our code in test class “TodoListRepositoryTests”. We add a private field “_sessionFactory” and then assign it a value in method “SetUp()”. The resulting file looks as follows:

Again, confirm with “dotnet test” in folder “/app” that everything still works.

Session and Session Factory in NHibernate

Both a session and session factory are key concepts in NHibernate. Conceptually a session represents a connection to a database. It is cheap to create. The session factory is an object that manages those sessions, e.g., it maintains a pool of session and decides whether a session can be reused. A session factory is fairly expensive to create in terms of runtime overhead. Generally, you will create just one instance within your application in most cases.

When we need a session, we’ll get it from the session factory. We then perform some work, e.g., we query for data, we add new objects, or we remove objects. Once we are finished with that work, we close the session to indicate that we are done with it. If we need another session, we just get a new one from the session factory.

Let’s return to the TodoListRepository. We need a new session for each the “Save()” as well as the “GetById()” method. A good option is to pass the session factory to the repository as a dependency in the constructor. Here is the repository class with that modification in place:

Accordingly, we need to change the code in the test class so we pass the session factory to the TodoListRepository constructor (line 18 in the following snippet):

And again, we confirm that our test passes by running “dotnet test” in directory “/app”.

Mapping between Objects and Table Rows

The next step is to update the implementation of methods “Save()” and “GetById()” in class TodoListRepository. As explained above this is what we need conceptually for each of the two methods:

Get a session from session factory Do work, e.g., query for data, persist objects, delete objects Close the session to indicate we no longer need it.

The code looks to be fairly simple, given that we have the session factory in the variable “_sessionFactory”:

With this pattern we can now change the implementation of the TodoListRepository to the following:

Note, how the “Save()” method returns the id of the persistent object. NHibernate takes care of generating the id. For most scenarios it is very useful to return the object id from this method as it tends to make the client code more readable. Note also, how in line 17 we call “session.Flush()” to ensure our changes are written to the database. In a future article we make this more explicit by adding explicit transaction handling. That will allows us to remove the “Flush()”-call.

If we run “dotnet test” now, the test will fail with an error message as follows:

The error message already gives a clue: NHibernate doesn’t know how to map the object to a row in a relational table. Let’s fix this.

In project “CmdLine” create a folder “Maps” inside the existing folder “DataAccess”, then add a class name “TodoListMap” with the following code:

To be on the safe side, here is the location of that file:

Note line 9: In a previous article we created the table with Fluent Migrator and gave it the name “List”. Line 9 maps class “TodoList” to the table “List” in the database. I use this for illustrative purposes here. We’ll resolve this in a future article which will allow us to remove line 9 from the map file.

Re-run “dotnet test”. This time we’ll notice a different error:

We can resolve this by changing the implementation of our domain class “TodoList” to the following:

Note, how we made both properties “virtual” (lines 7 and 8).

If we now run “dotnet test” in directory “/app” we will see the test pass:

Awesome! We just managed to make the first test succeed. We replaced the trivial implementation of the repository with a proper one that uses NHibernate as the object-relational mapper (ORM).

As we observed, this wasn’t too hard, given the meaningful error messages along the way that guided us to resolve those problems quickly.

We now have in place a test that demonstrates that we can store and read objects to and from a relational database. Before we summarize and look at next steps, allow me a couple of additional thoughts.