NHibernate Forge
The official new home for the NHibernate for .NET community

NHibernate Bootstrapper: UnitOfWork and SessionPerRequest

This is the first sample in a series of blogs about NHibernate in ASP.Net environment. This version gives a user new to NHibernate the absolute minimum pieces to start a well architected application. A number of the pieces necessary for an enterprise-capable application are not yet included, but the foundation has been laid. Source code and starter solution described in this post are available at NHibernateBootstrap Download.

The key pieces in this sample are demonstrations of the correct UnitOfWork pattern, using the NHibernate ISession based object with its transaction support. A DataAccessObject / Repository is also introduced that uses features from both styles of implementations and is also intended to work with Query Objects. The Web.Config file includes a starter configuration for NHibernate, using the LinFu proxy. The connection between the web application and NHibernate demonstrates a best practice, where the Session Per Request pattern is initialized in an IHttpModule - RequestHttpModule.cs. This is an implementation base on the ManagedWebSessionContext, and shows how to bind and unbind the session correctly.

This web application sample is not a best practices sample for ASP.Net development, but future blogs will add features, especially where they take advantage of an NHibernate implementation. Some of the implementation of the Person page is not following normal Microsoft recommended methods, and appear to be more complex than necessary. These changes are part of a further foundation and will allow the page to be transformed later where more than 95% of the logic normally encapsulated in the page behind code becomes testable. Initially there will be some references to NHibernate in the Web Application project, but this is not a recommended practice - later modifications will move these references so that they are in the BusinessServices project.

The one other item included in this sample is the Unit Test project. This test project connects to NHibernate using the ThreadStaticSessionContext. Each Test Fixture initializes its own ISession object and uses the same session object for all tests in that fixture. The app.config file for the test project, which configures the NHibernate support, is located in the bin/debug directory.

Solution Architecture

The Bootstrapper uses a multi-project structure and a library at the solution level to hold all the files:

  • NHibernate Bootstrap Solution

    • SharedLibs - a folder containing all external reference dll's and associated files

    • BootstrapperUnitTests Project - holds unit tests

    • Infrastructure Project - holds high level Interface and class files that may be used in multiple projects in the solution

    • DomainModel Project - holds the object model for the solution

    • NHibernateDAO Project - the persistence layer, gets data into and out of the database based on the object model and holds the mapping files

    • DataServices Project - isolates the persistence layer from the rest of the application

    • BusinessServices Project - provides an integration of the business logic with the data and controls the flow of information to and from the presentation layer

    • WebNHibernate Project - provides a web based presentation layer

Jason Dentler has written a series of blogs about using NHibernate, and I am borrowing from his discussion on DAOs, Repositories or Query Objects. The basic setup of the persistence layer uses a DAO-type FindBy approach augmented with support for QueryObjects. It is not my intent to discuss this approach now, but to cover it in a later post.

The purpose of this post is to discuss the ISession object and how to use it directly as a UnitOfWork, without an additional wrapper. The second purpose is to present a recommended solution for the SessionPerRequest architecture for an ASP.Net application.

SessionPerRequest Implementation

The SessionPerRequest implementation is recommended as a best practice to ensure that NHibernate is the most responsive and utilizes the least possible resources in the Web Server environment. The NHibernate.Context.ICurrentSessionContext is discussed in the reference document: NHibernate - Relational Persistence for Idiomatic .NET. The appropriate choices for a web application are either ManagedWebSessionContext or the WebSessionContext. (@Fabio: Can you explain the difference between the 2 - I have reviewed the code and see that ManagedWebSessionContext supports a GetExistingSession() - Does the WebSessionContext need to be explicitly bound and unbound in each request?)

Numerous examples of using ManagedWebSessionContext use the Global.asax to connect the NHibernate context to the web server. This is a bad practice in that it means the web application must have a direct reference to NHibernate.Context in order to work. It is instead recommended that an IHttpModule be created, and that the binding module be in the same project where the NHibernate reference is - in this sample that would be the NHibernateDOA project. This serves to isolate NHibernate from the web application entirely. All that is required is that the web application bin directory have a copy of the NHibernate dlls.

The code for the IHttpModule is shown below:

RequestHttpModule.cs
  1. using System;
  2. using System.Web;
  3. using NHibernate;
  4. using NHibernate.Context;
  5.  
  6. namespace NHibernateDAO
  7. {
  8.     public class RequestHttpModule : IHttpModule
  9.     {
  10.         public void Dispose()
  11.         {
  12.         }
  13.  
  14.         public void Init(HttpApplication context)
  15.         {
  16.             context.BeginRequest += new EventHandler(context_BeginRequest);
  17.             context.EndRequest += new EventHandler(context_EndRequest);
  18.         }
  19.  
  20.         public void context_BeginRequest(Object sender, EventArgs e)
  21.         {
  22.             ManagedWebSessionContext.Bind(HttpContext.Current, SessionManager.SessionFactory.OpenSession());
  23.         }
  24.  
  25.         public void context_EndRequest(Object sender, EventArgs e)
  26.         {
  27.             ISession session = ManagedWebSessionContext.Unbind(
  28.                 HttpContext.Current, SessionManager.SessionFactory);
  29.  
  30.             if (session != null)
  31.             {
  32.                 if (session.Transaction != null && session.Transaction.IsActive)
  33.                 {
  34.                     session.Transaction.Rollback();
  35.                 }
  36.                 else
  37.                     session.Flush();
  38.  
  39.                 session.Close();
  40.             }
  41.         }
  42.     }
  43. }
 

The use of the ManagedWebSessionContext is fairly straight forward. It is important to note that this file has a reference to System.Web, which should be the only reference to this library outside of the the WebApplication itself. It is undesirable to reference the System.Web library external to the web application project as it will make testing of those components with that reference very difficult.

The context_EndRequest also contains a Rollback call, which will ensure that any pending transactions are terminated without writing to the database if your application has an unhandled error.

In order for the custom IHttpModule to be active in the web server it must be registered in the Web.Config file:

Register RequestHttpModule
  1.   <httpModules>
  2.     <add name="RequestHttpModule" type="NHibernateDAO.RequestHttpModule, NHibernateDAO"/>
  3.   </httpModules>
  4.  
  5. </system.web>
  6.  
  7. <system.webServer>
  8.   <modules runAllManagedModulesForAllRequests="true">
  9.     <add name="RequestHttpModule" type="NHibernateDAO.RequestHttpModule, NHibernateDAO"/>
  10.   </modules>
  11. </system.webServer>

I have included the IHttpModule reference twice. The first time is for configurations of the web server using Classic Mode and the second is for IIS 7 Integrated Pipeline. See http://msdn.microsoft.com/en-us/library/ms227673.aspx

The final item necessary to make this work is an addition to the NHibernate configuration file. It is necessary to add the property

NHibernate ContextSession
  1. <property name="current_session_context_class">NHibernate.Context.ManagedWebSessionContext, NHibernate</property>
 

This is shown below in the Web.Config described in the Minimal Configuration section.

ISession UnitOfWork and Transactions

The proper management of database connections is a key component of ensuring a highly responsive web application in ASP.Net. http://nhforge.org/doc/nh/en/index.html#transactions gives some guidance, but does not provide any code samples or say what the recommended practice should be. This lack of direction is partly due to the possible scope of applications for NHibernate in general. However, in an ASP.Net environment the rules are fairly simple. First, you should SessionPerRequest as detailed above. Second, all database access should be wrapped in a transaction and that transaction should be committed when the connection to the database can be released. There is an almost infinite variation of implementations for these requirements in samples on the Internet. Invariably, programmers try to encapsulate the ISession object and transaction in a UnitOfWork class. While many of these samples are not incorrect in there implementation, some are, and many do not provide any additional functionality or save lines of code, when compared to a proper implementation of the bare ISession object. 

First some sample GenericDAO code (again, thanks to Jason Dentler, who provided the basic pattern). The declaration for the class:

Declaration for GenericDAOImp
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using DomainModel;
  6. using NHibernate;
  7.  
  8. namespace NHibernateDAO
  9. {
  10.     public class GenericDAOImpl<TEntity> : IRead<TEntity>, ISave<TEntity> where TEntity : Entity
  11.     {
  12.         public GenericDAOImpl(ISession Session)
  13.         {
  14.             m_Session = Session;
  15.         }
  16.  
  17.         protected readonly ISession m_Session;

A DAO implementation for NHibernate will usually have a GetByID, and this is no exception:

Function GetByID
  1. public TEntity GetByID(Guid ID)
  2. {
  3.     if (!m_Session.Transaction.IsActive)
  4.     {
  5.         TEntity retval;
  6.         using (var tx = m_Session.BeginTransaction())
  7.         {
  8.             retval = m_Session.Get<TEntity>(ID);
  9.             tx.Commit();
  10.             return retval;
  11.         }
  12.     }
  13.     else
  14.     {
  15.         return m_Session.Get<TEntity>(ID);
  16.     }
  17. }

The less normal GetBy query mechanism, is more normally part of a repository architecture, but makes for some very flexible code here:

GetBy Query Functions
  1. public IList<TEntity> GetByCriteria(ICriteria criteria)
  2. {
  3.     if (!m_Session.Transaction.IsActive)
  4.     {
  5.         IList<TEntity> retval;
  6.         using (var tx = m_Session.BeginTransaction())
  7.         {
  8.             retval = criteria.List<TEntity>();
  9.             tx.Commit();
  10.             return retval;
  11.         }
  12.     }
  13.     else
  14.     {
  15.         return criteria.List<TEntity>();
  16.     }
  17. }
  18.  
  19. public IList<TEntity> GetByQueryable(IQueryable<TEntity> queryable)
  20. {
  21.     if (!m_Session.Transaction.IsActive)
  22.     {
  23.         IList<TEntity> retval;
  24.         using (var tx = m_Session.BeginTransaction())
  25.         {
  26.             retval = queryable.ToList<TEntity>();
  27.             tx.Commit();
  28.             return retval;
  29.         }
  30.     }
  31.     else
  32.     {
  33.         return queryable.ToList<TEntity>();
  34.     }
  35. }

The Save function is the last part of the GenericDAOImpl class (note that there is no Delete function, as this capability is only exposed in the specific DAO classes actually requiring that function, rather than as a generic capability):

Save Function
  1. public TEntity Save(TEntity entity)
  2. {
  3.     if (!m_Session.Transaction.IsActive)
  4.     {
  5.         using (var tx = m_Session.BeginTransaction())
  6.         {
  7.             m_Session.SaveOrUpdate(entity);
  8.             tx.Commit();
  9.         }
  10.     }
  11.     else
  12.     {
  13.         m_Session.SaveOrUpdate(entity);
  14.     }
  15.     return entity;
  16. }

 

Each method in the GenericDAO class has 2 paths: one where the caller has wrapped the call in a transaction, and the other where no transaction was specified in the call. (Note: It is not necessary to check m_session.Transaction != null as the transaction will never be null if the ISession instance is valid, and the ISession instance must be valid to use the GenericDAO implementation.) It is left as an exercise for the reader to build a function to query using HQL.

An example test is shown below, first without an explicit transaction and then with a transaction:

PersonSaveTestWithoutTX
  1. [Test]
  2. public void PersonSaveTestWithoutTx()
  3. {
  4.     m_session = SessionManager.SessionFactory.GetCurrentSession();
  5.     Person _person = new Person();
  6.     _person.FirstName = "John";
  7.     _person.LastName = "Davidson";
  8.     _person.Email = "jwdavidson@gmail.com";
  9.     _person.UserID = "jwd";
  10.     PersonDAOImpl daoPerson = new PersonDAOImpl(m_session);
  11.     Person newPerson = daoPerson.Save(_person);
  12.  
  13.     Assert.AreEqual(_person, newPerson);
  14.     _person = null;
  15.     newPerson = null;
  16.     daoPerson = null;
  17.     m_session = null;
  18. }
 
Now with a transaction included in the test method:
 
PersonSaveTestWithTX
  1. [Test]
  2. public void PersonSaveTestWithTx()
  3. {
  4.     m_session = SessionManager.SessionFactory.GetCurrentSession();
  5.     Person _person = new Person();
  6.     _person.FirstName = "John";
  7.     _person.LastName = "Davidson";
  8.     _person.Email = "jwdavidson@gmail.com";
  9.     _person.UserID = "jwd";
  10.     PersonDAOImpl daoPerson = new PersonDAOImpl(m_session);
  11.     
  12.     using (var tx = m_session.BeginTransaction())
  13.     {
  14.         Person newPerson = daoPerson.Save(_person);
  15.         tx.Commit();
  16.         Assert.AreEqual(_person, newPerson);
  17.     }
  18.  
  19.     _person = null;
  20.     daoPerson = null;
  21.     m_session = null;
  22. }
 

There are 2 additional lines of code in the sample with a direct call to the transaction. Any other UnitOfWork implementation cannot improve on this and still work correctly.

Minimal NHibernate Configuration

NHibernate needs a program to read the configuration and initialize the SessionFactory. This is called the SessionManager.cs is shown below the configuration and unit test setup code.

The web.config file holds the configuration details as shown below:

NHibernate in Web.Config
  1. <configSections>
  2.   <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
  3. </configSections>
  4.   <hibernate-configurationxmlns="urn:nhibernate-configuration-2.2" >
  5.   <session-factory name="NHibernate.Bootstrapper">
  6.     <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
  7.     <property name="connection.connection_string">
  8.       Data Source=.\SQLEXPRESS;Initial Catalog=NHibernateBootstrapper;Integrated Security=True;Pooling=True
  9.     </property>
  10.     <property name="show_sql">false</property>
  11.     <property name="adonet.batch_size">10</property>
  12.     <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
  13.     <property name="use_outer_join">true</property>
  14.     <property name="command_timeout">60</property>
  15.     <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
  16.     <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
  17.     <property name="current_session_context_class">NHibernate.Context.ManagedWebSessionContext, NHibernate</property>
  18.     <property name="generate_statistics">false</property>
  19.   </session-factory>
  20. </hibernate-configuration>
 

The Unit Test project has a similar configuration, but it is stored in an app.config file in the bin directory with some minor changes. The show_sql property is set to true, causing the sql to be output in the NUnit console display. The second change is that the property current_session_context_class is set to ThreadStaticSessionContext. In this case the current context needs it own setup:

Setup / Teardown for UnitTest
  1. private ISession m_session;
  2.  
  3. [TestFixtureSetUp]
  4. public void TestFixtureSetup()
  5. {
  6.     var session = SessionManager.SessionFactory.OpenSession();
  7.     CallSessionContext.Bind(session);
  8. }
  9.  
  10. [TestFixtureTearDown]
  11. public void TestFixtureTeardown()
  12. {
  13.     var session = CallSessionContext.Unbind(SessionManager.SessionFactory);
  14.     if (session != null)
  15.     {
  16.         if (session.Transaction != null && session.Transaction.IsActive)
  17.         {
  18.             session.Transaction.Rollback();
  19.         }
  20.         else
  21.             session.Flush();
  22.  
  23.         session.Close();
  24.     }
  25.  
  26.     session.Dispose();
  27. }

I waited to show the SessionManager.cs code, as it was necessary to understand how the Unit Tests work with the NHibernate DAO classes, in a nearly transparent manner. This SessionManager.cs was adapted from a blog post by Petter Wiggle. The reference post titled ‘NHibernate Session handling in ASP.NET – the easy way’ is one of the examples of how not to implement SessionPerRequest as it uses the Global.asax file for initialization, rather than correctly using an IHttpModule. Now for the code (split into 3 section to ensure that it will print correctly):

SessionManager.cs (Part 1)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using NHibernate;
  7. using NHibernate.Cfg;
  8.  
  9. namespace NHibernateDAO
  10. {
  11.     public sealed class SessionManager
  12.     {
  13.         private readonly ISessionFactory sessionFactory;
  14.         public static ISessionFactory SessionFactory
  15.         {
  16.             get { return Instance.sessionFactory; }
  17.         }
  18.  
  19.         private ISessionFactory GetSessionFactory()
  20.         {
  21.             return sessionFactory;
  22.         }
  23.  
  24.         public static SessionManager Instance
  25.         {
  26.             get { return NestedSessionManager.sessionManager; }
  27.         }
  28.  
  29.         public static ISession OpenSession()
  30.         {
  31.             return Instance.GetSessionFactory().OpenSession();
  32.         }
SessionMnager.cs (Part 2)
  1. private SessionManager()
  2. {
  3.     if (sessionFactory == null)
  4.     {
  5.         Configuration configuration;
  6.         if (AppDomain.CurrentDomain.BaseDirectory.Contains("UnitTest"))
  7.         {
  8.             configuration = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app.config"));
  9.             log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app.config")));
  10.         }
  11.         else
  12.         {
  13.             configuration = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config"));
  14.             log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config")));
  15.         }
  16.         //Configuration configuration = new Configuration().Configure();
  17.         if (configuration == null)
  18.         {
  19.             throw new InvalidOperationException("NHibernate configuration is null.");
  20.         }
  21.         else
  22.         {
  23.             configuration.AddAssembly("NHibernateDAO");
  24.             sessionFactory = configuration.BuildSessionFactory();
  25.             if (sessionFactory == null)
  26.                 throw new InvalidOperationException("Call to BuildSessionFactory() returned null.");
  27.         }
  28.     }
  29. }
SessionManager.cs (Part 3)
  1.         class NestedSessionManager
  2.         {
  3.             internal static readonly SessionManager sessionManager = new SessionManager();
  4.         }
  5.     }
  6. }

The SessionManager gets its configuration data from the Unit Test project if "UnitTest" is in the application directory name, otherwise it performs a normal configuration and uses the web.config for the Web Application. It is that easy.

Application Domain Setup

The map file is one method to notify NHibernate of the class to table mapping. Other methods are ConfORM or FluentNHibernate, but the map file was the first mechanism. Remember to set the Build Action for your map files to Embedded Resource so that they are included in the assembly file to be read by the SessionManager in the initialization process.

Person.hbm.xml
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel"
  3.     namespace="DomainModel.Person">
  4.  
  5.   <class name="Person" table="Person" >
  6.     <id name="Id" column="Person_ID">
  7.       <generator class="guid.comb"/>
  8.     </id>
  9.     <property name="FirstName" column="First_Name" />
  10.     <property name="LastName" column="Last_Name" />
  11.     <property name="Email" column="Email" />
  12.     <property name="UserID" column="User_ID"/>
  13.   </class>
  14. </hibernate-mapping>
 

Here you can see the table diagram for the Person table in the SQL Server database.

Person

The class diagram for the Person class shows how the map file uses both the table and the class definition to make NHibernate able to persist an instance of the Person class. Also note that the Person Class inherits from the Entity class.

PersonCls

I have borrowed the Entity class from the implementation of Jason Dentler, described earlier. This Entity class is slightly different from a number of Id providers used by NHibernate persistence classes in that it makes the setter for the Id property ‘protected’, rather than ‘public’. This means that the application code is not able to directly set the Id of a class instance, but that it can only be set by the DAO implementation. As a result the application is forced to use Data Transfer Objects (dto) to communicate from the UI to the persistence classes.

Entity.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace DomainModel
  7. {
  8.     public abstract class Entity
  9.     {
  10.         private Guid m_ID;
  11.  
  12.         public virtual Guid Id
  13.         {
  14.             get { return m_ID; }
  15.             protected set { m_ID = value; }
  16.         }
  17.     }
  18. }

 

The PersonDto class diagram is shown below. The PersonID property maps to the Person.Id property of the persistence class, with all other properties lining up as expected.

PersonDtoCls

The next 3 code snippets show how a dto is used to move data from a persistence class to a dto and then use that dto to populate a DataGrid. The function Get_PersonData fills a persistence class from the database using the ICriteria based GetByCriteria query and then uses a LINQ to Object function to fill the dto. Then the dto is passed to the DataGrid and then it is data bound in the method Fill_gvPerson.

Function Get_PersonData()
  1. public IList<PersonDto> Get_PersonData()
  2. {
  3.     IList<PersonDto> retVal = null;
  4.  
  5.     if (m_session == null)
  6.         m_session = SessionManager.SessionFactory.GetCurrentSession();
  7.  
  8.     ICriteria crit = m_session.CreateCriteria(typeof(Person));
  9.     PersonDAOImpl dao = new PersonDAOImpl(m_session);
  10.  
  11.     IList<Person> people = dao.GetByCriteria(crit);
  12.     retVal = (from person in people
  13.               select new PersonDto
  14.                   {
  15.                       PersonID = person.Id,
  16.                       FirstName = person.FirstName,
  17.                       LastName = person.LastName,
  18.                       Email = person.Email,
  19.                       UserID = person.UserID
  20.                   }).ToList<PersonDto>();
  21.     crit = null;
  22.     dao = null;
  23.     people = null;
  24.  
  25.     return retVal;
  26. }

 

Calling GetPersonData()
  1. if (!Page.IsPostBack)
  2. {
  3.     IList<PersonDto> gvData = Get_PersonData();
  4.     Fill_gvPerson(gvData);
  5. }

 

Fill Grid with DTO Data
  1. public void Fill_gvPerson(IList<PersonDto> data)
  2. {
  3.     gvPerson.DataSource = data;
  4.     gvPerson.DataBind();
  5. }

 

The method _view_OnSaveEditPerson demonstrates the necessary steps to get data out of a dto and then to persist the user inputs. First it checks the value of txtPersonIdValue to determine if the data is from an instance that exists in the persistence layer, or is it a new instance – indicated by an empty value. Next the persistence class Person instance is fill using a GetByID function from the DAO implementation and then the data from the form fields is copy to the Person instance. Finally the Person instance is now saved.

 

 

 

 

 

 

 

 

 

 

 

Save Entity from Form Data
  1. public void _view_OnSaveEditPerson()
  2. {
  3.  
  4.     if (m_session == null)
  5.         m_session = SessionManager.SessionFactory.GetCurrentSession();
  6.  
  7.     Guid editId = new Guid();
  8.     Person editPers = new Person();
  9.  
  10.     if (!string.IsNullOrEmpty(txtPersonIdValue))
  11.         editId = new Guid(txtPersonIdValue);
  12.  
  13.     PersonDAOImpl dao = new PersonDAOImpl(m_session);
  14.     using (var tx = m_session.BeginTransaction())
  15.     {
  16.         if (editId.ToString().Length == 36)
  17.             editPers = dao.GetByID(editId);
  18.         editPers.FirstName = txtFirstNameValue;
  19.         editPers.LastName = txtLastNameValue;
  20.         editPers.Email = txtEmailValue;
  21.         editPers.UserID = txtUserIdValue;
  22.  
  23.         editPers = dao.Save(editPers);
  24.         tx.Commit();
  25.     }
  26.  
  27.     editPers = null;
  28.     dao = null;
  29.     _view_OnRefreshPersonGrid();
  30.     _view_OnClearEditPerson();
  31. }

Next Steps

While the sample solution is useful, it is definitely not enterprise ready. The main problem with this solution is that the Web Application has a knowledge of the persistence layer. The next step is to factor these calls to the BusinessServices project. Doing that will reduce the interdependencies and will increase the overall testability of the full application, as it is almost impossible to properly unit test code in the Web Application itself. Additionally further decoupling can be achieved by ensuring that all calls to the persistence layer are done through the DataServices layer.


Posted jul 11 2010, 04:17 p.m. by John Davidson

Comments

Bogdan Marian wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-12-2010 3:58

Very nice article! I really like having an IHttpModule to encapsulate NH session management. I do have a question: why opening a transaction and committing it right away ? I would have expected to start the transaction when the NH session is called for the first time to perform a CRUD operation and continue using that transaction for until committing it before the session gets closed...

JorgeVinagre wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-12-2010 10:30

Hi

I know that session per request managment is considered a good practice. Isn´t that true also that session per business process is considered a best practice also?

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-12-2010 18:49

@Bogdan Marian

This is constructed sothat if you do not want to manage transactions then NHibernate will function correctly with no performance penalties, but if you want to manage your transactions, either in your DataLayer or DataServices leyer then you can open a single trnasaction, perform a number of persistence operations and then commit before closing the session.

@JorgeVinagre

SessionPerRequest is for an ASP.Net application. Each form request should be a single business process.

MattO wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-16-2010 18:10

This seems to be a bit different (implementing in an httpmodule) compared to what is described here:

www.codeproject.com/.../NHibernateBestPractices.aspx

The codesmith templates follow most of what is described in the codeproject article listed above.  

Which is better?  Why choose your way over the way CodeSmith is doing it now by following the "best practices" as detailed in the codeproject article?

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-16-2010 18:43

@MattO

The CodeProject article written by Bill McCafferty was published in Mar 2006. It did not take into account the new capability introduced with the 1.2 version of the current_session_context_class. Not much has been written about this class and almost all of the articles that recognized what this class is for, showed implementations using Global.asax. What makes this approach better is that all reference to NHibernate is removed from the web application - this is not fully realized in the demo application, but those deficiencies are noted and will be corrected as development of the application progresses. If the Codesmith templates are still following the practices outlined here then perhaps they are due for some updating?

I use much of Bill McCafferty's other ideas, especially with regard to Model-View-Presenter and will expand on these in the next post.

David W Martines wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-18-2010 15:32

I'd like to add my current UnitOfWork implementation to the list of options.  I find that it is simple and effective and can be used per web request or per business transaction, whichever you prefer.  I invite you to take a look and  comment or point out any flaws.

blogs.businesssoftwareengineering.com

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-18-2010 21:30

See the nhusers maillist thread at groups.google.com/.../48e414f480a24950.

The point of the blog is that a UnitOfWork is no longer required. Everything that your UnitOfWork does is already in ISession. The requirement for a separate UnitOfWork became deprecated with the release of NHibernate 1.2, but old habits die hard.

Rather than repeating the transaction management function of the ISession object in your UnitOfWork, you could spend time on transaction management in the Repository/DAO layer.

If you need a UnitOfWork so that your IoC container will work correctly, then you probably need a different container or a better implementation of how you use the container.

MattO wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-22-2010 18:13

I'm struggling with the general stateless nature of ASP .NET and how to work with nHibernate and it seems to still be present after reading this article.

My issue is the following:

Say I retrieve data from the database and display it on the web form using ASP .NET.  A user then edits some data on the webform and hits the SAVE button.  It does a post back.

At this point I need to save the data to the database, my session has already disappeared as it only lived through the single request.  And this article states we should use SessionPerRequest.  But how can you do this if you want to do the following things:

Only commit the changes if someone hasn't already changed the data.  For instance, say I retrieve the data, and left it open in my web browser for a minute and then hit the save button, but during that time 3 other people already modified that record in some way.  I need to be able to ensure that I don't overwrite their changes.  How do you handle this?  How can I have nHibernate know that the timestamp on this data has changed and therefore to tell the user to reload the page and try edit the data again?

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-22-2010 19:22

@MattO

You are right that your difficulty understanding is an ASP.Net issue rather than an NHibernate issue, as the problem you describe exists in any web application where concurrency and volatility of a single instance is high.

In this case NHibernate facilitates a solution. See nhforge.org/.../index.html. Perhaps you are forgetting that NHibernate closes the session when the web page is displayed. So even if you keep the page open if you want to use the entity that was displayed on that page you must reload it into the session by reading it again from the database, hence the discussion about DTO in the post above. Then when it is loaded you can compare the current timestamp with the timestamp retrieved from the web page and then act according to your business rules. In this case advise the user that the data had changed by another user and then display it on the page again for edit, with the new timestamp.

Hope this helps

MattO wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-22-2010 20:41

Thanks John for all the replys!

A few (hopefully last) questions:

1.  Have you found that instantiating a nHibernate session so many more times in a stateless application (because you constantly have to refresh the data from the database on each postback) causes performance issues?

2.  Also even if I have to refresh the data from the database, which I'm perfectly fine with.  How can I have nHibernate automatically know the timestamps are different and have it throw the concurrency violation?  The reason why I ask is that compare timestamp columns as byte arrays is a bit cumbersome.

3.  In relation to #2, if I wasn't using a timestamp column because i'm dealing with a table that doesn't have it and there is no other way to check for uniqueness other then checking for differences in EVERY column of say a 50 column table, are my only options is to go through all 50 column propertys we just reloaded from the database on postback to determine if there is a concurrency violation or is there some way in which nHibernate can facilitate this rather then writing a very lengthy IF statement for every property in the table?

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 07-22-2010 21:23

@MattO

I find that using NHibernate for an ASP.Net application improves performance because it reduces the object-relation impedance mismatch. There is no additional performance hit over a system not using NHibernate as it must make the same number of data connections in order to maintain data consistency. NHibernate formalizes the necessary steps.

In order to effectively compare values of objects, or even complex strings, you should use Object.GetHashCode. See msdn.microsoft.com/.../system.object.gethashcode.aspx, which allows comparisons without going property by property. Note that if you do not use a version property or timestamp you cannot determine which difference is the one you want to store unless you also store the original object. You would need the original object, the object edited on the web page and the object newly obtained from the database. It is far simpler to modify your database. Even with a legacy system it should be possible to add a version or timestamp field. NHibernate will not do the detection automatically unless you are using pessimistic locking, which I would not recommend in an ASP.Net application.

Raoul Buzziol wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 08-11-2010 5:54

Thank you for the nice article.

@JohnDavidson

Just a consideration about your style of transaction management. We found that avoiding transaction management in Daos and using a separated session (and transaction) manager resulted in less code and less duplication. The approach is inpired by www.codeproject.com/.../NHibernateBestPractices.aspx and community.jboss.org/.../sessionsandtransactions . What kind of benefits do you have with your style?

@MattO

We don't hit the db on every page relaod using the model manager by Spring.Net. See www.springframework.net/.../web.html

Instead of a timestamp you can use the version nhforge.org/.../index.html element. But the versioning handles conflict for rows not columns and that's correct. If a column changes in a DDD oriented approach the state of your model has changed you must revalidate it, involving probably other properties too.

Conflict resolution can be complex and how do you handle it depens on the functional/non-functional requirements. You can find some ideas here nhforge.org/.../index.html .

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 08-11-2010 9:18

@Raoul

You are missing the point I am trying to make. NHibernate includes an already built session and transaction manager, and it does not need to be abstracted or extended. It just works - and it requires _no_ additional code.

Raoul Buzziol wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 08-11-2010 9:42

You are right: it requires no additional code.

But you are handling transaction aspects inside _your_ dao with lines like:

if (!m_Session.Transaction.IsActive)

using (var tx = m_Session.BeginTransaction())

tx.Commit();

witch we only do outside, simplifying our dao. Our daos know nothing about transactions.

And duplication like:

m_Session.SaveOrUpdate(entity);

would be no more necessary.

It's not wrong, just different. I was asking myself when your style would pay off over my style.

Eric wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 08-19-2010 14:02

I was wondering if you can offer any insight into how this would need to change for multiple DB support (one app, multiple dbs on different servers). I was reading Bill McCafferty's article on Codeproject (www.codeproject.com/.../NHibernateMultipleDBs.aspx), but that's 1.2 and I'm not quite sure how to merge your solution and his into a best-of-both result.

Further, I'm not sure if NH2.0 adds anything significant to help with multiple DB access.

Any and all help is appreciated.

John Davidson wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 08-19-2010 18:24

@Eric

A better implementation for multiple databases is to use the ISessionFacory as it was designed to be used. Rather than the article you reference see instead fabiomaulo.blogspot.com/.../configure-sessionfactory-providers.html

The sample that I have would need to be extended for each session factory instantiated. However, I would be wary about creating an architecture using a web implementation for multiple databases, especially if this is your first NHibernate project.

OS538DAV wrote re: NHibernate Bootstrapper: UnitOfWork and SessionPerRequest
on 09-14-2010 11:05

Download doesn't work at public.me.com/jw_davidson

David

NHibernate blog wrote NHibernate Bootstrapper: Unit Tests and Project References
on 12-05-2010 13:08

This post is the second one about the NHibernate Bootstrapper. The first is located here . The first

article source wrote article source
on 09-28-2014 5:13

NHibernate Bootstrapper: UnitOfWork and SessionPerRequest - NHibernate blog - NHibernate Forge

Powered by Community Server (Commercial Edition), by Telligent Systems