Using NHibernate in an ASP.NET MVC 4 Application

Now a days ASP.NET MVC is a common framework for developing web applications. As we all know that M in MVC stands for model, and for a Line of business (LoB application ), the model is often the backbone. For defining a model that persists in a database, ASP.NET MVC supports Entity Framework (now Open Source) out of the box. Other possible options are:

ADO.NET hand coded data layer where you write all the queries explicitly.
  • Use commercial O/R mappers like LLBLGen
  • Use Open Source O/R mapper like NHibernate.
Today we will see how we can get started using NHibernate

Getting Started with NHibernate

You can get additional information about the NHibernate from here.

Once you identify various models and relationship in your data model, you can now introduce NHibernate for mapping these models to a persistence store like (SQL Server, Oracle etc).
One of the most important part of using ORM is establishing mapping between Model objects and the Database tables. If we use NHibernate, then this mapping needs to be set explicitly using XML file.

In the following steps, we will be exploring use of NHibernate in ASP.NET MVC 4 application.

Step 1: Open VS 2012 and create MVC 4 application. On the project, right click and select  Manage NuGet Packages’. You will see the Manage Nuget Packages screen. In the search TextBox enter ‘NHibernate’ and you will get the following result:

nhibernate-package

Once you click the ‘Install’, you will get below references in then project:
  • NHibernate
  • Lesi.Collections
Step 2: Now it is a time to define the Model layer. As I wrote in the beginning that based upon the application requirement, you need to decide upon the Model objects and the relationship between them. So now let’s define an application that is used to maintain employee records (very simple, but you can extend the concept). Let’s add the new class in the Models folder as shown below:

public class EmployeeInfo
{
int _EmpNo;
public virtual int EmpNo
{
  get { return _EmpNo; }
  set { _EmpNo = value; }
}
string _EmpName;
public virtual string EmpName
{
  get { return _EmpName; }
  set { _EmpName = value; }
}
int _Salary;

public virtual int Salary
{
  get { return _Salary; }
  set { _Salary = value; }
}
string _DeptName;

public virtual string DeptName
{
  get { return _DeptName; }
  set { _DeptName = value; }
}
string _Designation;

public virtual string Designation
{
  get { return _Designation; }
  set { _Designation = value; }
}
}

The class EmployeeInfo contains properties. These properties will be used for mapping with the Table Columns. These properties are defined as virtual properties because of the lazy association which is used by NHibernate to set proxy entity on association property.

Step 3: Once the Model class for mapping is ready, now let’s think of the database to persist the data. For this simple application, we will use a database called Company in SQL Server. The name of the table is EmployeeInfo which can be created as shown below:

USE [Company]
GO
/****** Object:  Table [dbo].[EmployeeInfo]    Script Date: 1/17/2013 11:22:12 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[EmployeeInfo](
    [EmpNo] [int] IDENTITY(1,1) NOT NULL,
    [EmpName] [varchar](50) NOT NULL,
    [Salary] [decimal](18, 0) NOT NULL,
    [DeptName] [varchar](50) NOT NULL,
    [Designation] [varchar](50) NOT NULL,
CONSTRAINT [PK_EmployeeInfo] PRIMARY KEY CLUSTERED
(
    [EmpNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

Step 4: To set the mapping, we need to add an XML file in the project as ‘Embedded Resource’. For this sample, I’ll use two folders under the default Model folder - NHibernate\Configuration and NHibernate\Mappings.

The naming convention for the mapping files are by default <ModelName>.hbm.xml, so in our case it will be ‘EmployeeInfo.hbm.xml’. This file goes into the Mappings folder. This file maps the Model class with the database table columns with the constraints like primary key, data types etc. The file is as shown below:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
assembly="MVC4_Using_NHB"
namespace="MVC4_Using_NHB"
auto-import="true">
<class name="MVC4_Using_NHB.Models.EmployeeInfo,MVC4_Using_NHB">
  <id name="EmpNo" access="property" column="EmpNo" type="Int32">
   <generator class="native"></generator>
  </id>
  <property name="EmpName" access="property"
   column="EmpName" type="String"></property>
  <property name="Salary" access="property"
   column="Salary" type="Int32"></property>
  <property name="DeptName" access="property"
   column="DeptName" type="String"></property>
  <property name="Designation" access="property"
   column="Designation" type="String"></property>
  </class>
</hibernate-mapping>

The above xml file demonstrates the mapping between EmployeeInfo class and its properties with the columns. The mapping table is defined by the NHibernate APIs while establishing connection to database.

Note: One important thing is that by default no intellisense is available so to achieve this add nhibernate-configuration.xsd and nhibernate-mapping.xsd in below path

C:\Program Files (x86)\Microsoft Visual Studio 11.0\Xml\Schemas

Step 5: Once the mapping is defined, now let’s define the NHibernate configuration for the application. This provides information about the database provider, connection string and the mapping file used for the connectivity. So in the project, add a new XML file in the Models\Configuration folder created above; the name of the file will be ‘hibernate.cfg.xml’. Add the following configuration in it:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
  <property name="connection.provider">

NHibernate.Connection.DriverConnectionProvider</property>
  <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
  <property name="connection.driver_class">

NHibernate.Driver.SqlClientDriver</property>
  <property name="connection.connection_string">Data Source=.;Initial Catalog=Company;Integrated Security=SSPI</property>
  <property name="show_sql">false</property>
</session-factory>
</hibernate-configuration>


Step 6: Now it’s time to add some code to do CRUD operations against the database table using the mapping model. NHibernate provides various classes and interfaces for performing operations, some of them are used in this implementation and they are as below:

ISession: This is a main runtime interface between NHibernate and .NET and is used to manipulate entities.

ISessionFactory: A Session is created by this interface. The method ‘OpenSession()’ is provided to create session. One session factory is required per database. The implementation is thread safe and can live till the life time of the application.

As you can see in the code below, we have provided the absolute path of the configuration file to the configuration object and also provided it with the Directory Information where all the mapping files will be kept (in the OpenSession method).

IQuery: This is an object representation of NHibernate query. This is created using ‘CreateQuery()’ method of the ISession. This is the method where the table name is passed and based upon which column the mapping can take place.

ITransaction: Used to manage transactions. This is required during DML operations.
Add a new class in the Models folder and add the following code in it:

/// <summary>
/// class to perform the CRUD operations
/// </summary>
public class EmployeeInfoDAL
{
//Define the session factory, this is per database
ISessionFactory sessionFactory;
/// <summary>
/// Method to create session and manage entities
/// </summary>
/// <returns></returns>
ISession OpenSession()
{
  if (sessionFactory == null)
  {
   var cgf = new Configuration();
   var data = cgf.Configure(
         HttpContext.Current.Server.MapPath(
            @"Models\NHibernate\Configuration\hibernate.cfg.xml"));
   cgf.AddDirectory(new System.IO.DirectoryInfo(
         HttpContext.Current.Server.MapPath(@"Models\NHibernate\Mappings")));
   sessionFactory = data.BuildSessionFactory();
  }
  return sessionFactory.OpenSession();
}
public IList<EmployeeInfo> GetEmployees()
{
  IList<EmployeeInfo> Employees;
  using (ISession session = OpenSession())
  {
   //NHibernate query
   IQuery query = session.CreateQuery("from EmployeeInfo");
   Employees = query.List<EmployeeInfo>();
  }
  return Employees;
}
public EmployeeInfo GetEmployeeById(int Id)
{
  EmployeeInfo Emp = new EmployeeInfo();
  using (ISession session = OpenSession())
  {
   Emp = session.Get<EmployeeInfo>(Id);
  }
  return Emp;
}
public int CreateEmployee(EmployeeInfo Emp)
{
  int EmpNo = 0;
  using (ISession session = OpenSession())
  {
   //Perform transaction
   using (ITransaction tran = session.BeginTransaction())
   {
    session.Save(Emp);
    tran.Commit();
   }
  }
  return EmpNo;
}

public void UpdateEmployee(EmployeeInfo Emp)
{
  using (ISession session = OpenSession())
  {
   using (ITransaction tran = session.BeginTransaction())
   {
    session.Update(Emp);
    tran.Commit();
   }
  }
}

public void DeleteEmployee(EmployeeInfo Emp)
{
  using (ISession session = OpenSession())
  {
   using (ITransaction tran = session.BeginTransaction())
   {
    session.Delete(Emp);
    tran.Commit();
   }
  }
}
}

Build the project and make sure that it is error free.

Step 7: Add a new Controller in the Controllers folder, name it as ‘EmployeeInfoController’. Add the following action methods in the controller class:

using MVC4_Using_NHB.Models;
using System.Web.Mvc;

namespace MVC4_Using_NHB.Controllers
{
public class EmployeeInfoController : Controller
{
  EmployeeInfoDAL objDs;
  public EmployeeInfoController()
  {
   objDs = new EmployeeInfoDAL();
  }
  //
  // GET: /EmployeeInfo/
  public ActionResult Index()
  {
   var Employees = objDs.GetEmployees();
   return View(Employees);
  }
//
// GET: /EmployeeInfo/Details/5

public ActionResult Details(int id)
{
  return View();
}
//
// GET: /EmployeeInfo/Create
public ActionResult Create()
{
  var Emp = new EmployeeInfo();
  return View(Emp);
}
//
// POST: /EmployeeInfo/Create
[HttpPost]
public ActionResult Create(EmployeeInfo Emp)
{
  try
  {
   objDs.CreateEmployee(Emp);
   return RedirectToAction("Index");
  }
  catch
  {
   return View();
  }
}
//
// GET: /EmployeeInfo/Edit/5
public ActionResult Edit(int id)
{
  var Emp = objDs.GetEmployeeById(id);
  return View(Emp);
}
//
// POST: /EmployeeInfo/Edit/5
[HttpPost]
public ActionResult Edit(int id, EmployeeInfo Emp)
{
  try
  {
   objDs.UpdateEmployee(Emp);
   return RedirectToAction("Index");
  }
  catch
  {
   return View();
  }
}

//
// GET: /EmployeeInfo/Delete/5
public ActionResult Delete(int id)
{
  var Emp = objDs.GetEmployeeById(id);
  return View(Emp);
}
//
// POST: /EmployeeInfo/Delete/5
[HttpPost]
public ActionResult Delete(int id,FormCollection collection)
{
  try
  {
   var Emp = objDs.GetEmployeeById(id);
   objDs.DeleteEmployee(Emp);  
   return RedirectToAction("Index");
  }
  catch
  {
   return View();
  }
}
}

Each action method makes a call to the method defined in the EmployeeInfoDAL class. That’s it. Now add views for each action method and test them.

nhibernate-mvc-demo

Conclusion

We saw how easy it is to make use of NHibernate in MVC application for building Line of Business (LOB) applications.

The Sample code for this application is at https://github.com/devcurry/introducing-nhibernate-mvc




About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

10 comments:

Anonymous said...

Thanks for taking the time to write this up.

Rohit Kesharwani said...

Nice blog for freshers.
Please help me. I am getting this error while performing DML operation:

An exception occurred during configuration of persistence layer.

Thanks in advance.

Unknown said...

Nice article!. One Important typo is that you have mentioned "Lesi.Collections" instead of Iesi.Collections i.e. you have used "L" instead of "I" since it contains some special implementations of ISet.

Anonymous said...

@Rohit
I had the same problem but substuting this:
System.Web.Hosting.HostingEnvironment.MapPath("~/Models/NHibernate/Configuration/hibernate.cfg.xml")

for this:
HttpContext.Current.Server.MapPath( @"Models\NHibernate\Configuration\hibernate.cfg.xml"));

Seemed to help

Anonymous said...

The web.config in your solution is still using the Entity Framework. You never covered removing that reference. How do you know that you're actually using NHibernate to get the data?

Unknown said...

how to create view pls, send instruction to my mail yantarb@rambler.ru

ty!

Unknown said...

Hi. I've created a MVC Internet Project from one of VS 2012 templates. I've added NHibernate as you explained and also I've changed this HttpContext.Current.Server.MapPath( @"Models\NHibernate\Configuration\hibernate.cfg.xml"));
for this

HttpContext.Current.Server.MapPath( @"~\Models\NHibernate\Configuration\hibernate.cfg.xml"));

as one of the above comment said.

But it give me an exception at this line
sessionFactory = data.BuildSessionFactory ();

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections.

I've google it but I'm using a default database created by VS for this template and I don't have acces to it's properties to make thouse modifications sugested by the people who had the same problem.
Any idea how to fix this. Thanks.
PS: Nice job!

Unknown said...

I've fix it. The problem was in my cfg file. In the connection.string I've copied the connection string provided by VS in server explorer database properties connection. Hope this helps.

Unknown said...

Important thing!!! If you will user many-to-one or one-to-many relations mapping use fully qualified names for your entities. I've spend hours for not doing so :). Ex: instead of just class="EmployeeInfo" use class="yourProjectName.Models.EmployeeInfo".
Hope this helps!

Unknown said...

I am new for MVC so please help me for View code fot this demo..