NHibernate Session Handling Revisited

In an old blog post I described how you can implement the Open Session in View pattern using Contextual Sessions. Since then I have discovered even easier way to handle the NHibernate sessions in an ASP.NET web application. The approach I have used recently is the one proposed by Ayende in this blog post. His approach is to store the reference to the current session in the current HttpContext and hooking in the session lifecycle management into the BeginRequest and EndRequest events.

Another approach that I haven’t used yet but I certainly will try in some upcoming project is to let your IoC container manage the sessions. Here is an example of how to do that with StructureMap.

NHibernate Session handling in ASP.NET – the easy way

EDIT: Take a look at a new blog post of mine on this subject.

When I first started using the popular ORM framework NHibernate, I was a bit confused about the best way to manage NHibernate sessions in a web application. The official documentation did not provide much guidance, but I did find out that the pattern Open Session In View was the way to go. The obvious question was: how do I implement this pattern in the context of an ASP.NET application?

The implementation that was proposed in the otherwise great article NHibernate Best Practices seemed overly complicated. There must be a simpler way to do this. After reading the documenation again I found that there is a feature in NHibernate called Contextual Sessions.

The basic idea with contextual sessions is that in order to get a reference to an instance of ISession you simply call the method GetCurrentSession() that is defined in the interface ISessionFactory. The call to GetCurrentSession() is delegated to a class specified by the configuration parameter hibernate.current_session_context_class. This class implements the interface NHibernate.Context.ICurrentSessionContext. Out-of-the-box NHibernate provides one implementation of this interface, NHibernate.Context.ManagedWebSessionContext, which tracks current sessions by HttpContext. That is exactly what we want in order to implement the Open Session In View pattern.

What we need to do in order to utilize the ManagedWebSessionContext is to bind the current HttpContext to an open session at the beginning of each request and then unbind the session to the context at the end of each request. The easiest way to do this is to use the Application_BeginRequest and Application_EndRequest event handlers in Global.asax. You could of course implement your own HttpModule and do the bindind and unbinding there, if you for some reason don’t want to use Global.asax.

Now it is time to show you some code! First of all you have to implement a session manager. I have implemented the session manager as a lazy, lock-free, thread-safe singleton:

using NHibernate;
using NHibernate.Cfg;

namespace DataAccess
{

    public class SessionManager
    {
        private readonly ISessionFactory sessionFactory;
        public static ISessionFactory SessionFactory
        {
            get { return Instance.sessionFactory; }
        }

        private ISessionFactory GetSessionFactory()
        {
            return sessionFactory;
        }

        public static SessionManager Instance
        {
            get {
                return NestedSessionManager.sessionManager; }
        }

        public static ISession OpenSession()
        {
            return Instance.GetSessionFactory().OpenSession();
        }

        public static ISession CurrentSession
        {
            get
            {
                return Instance.GetSessionFactory().GetCurrentSession();
            }
        }

        private SessionManager()
        {
            Configuration configuration = new Configuration().Configure();
            sessionFactory = configuration.BuildSessionFactory();
        }

        class NestedSessionManager
        {
            internal static readonly SessionManager sessionManager =
                new SessionManager();
        }
    }
}

The binding and unbinding of the NHibernate session to the current HttpContext is done in Global.asax:

        protected void Application_BeginRequest(
            object sender, EventArgs e)
        {
            ManagedWebSessionContext.Bind(
                HttpContext.Current,
                SessionManager.SessionFactory.OpenSession());
        }

        protected void Application_EndRequest(
            object sender, EventArgs e)
        {
            ISession session = ManagedWebSessionContext.Unbind(
                HttpContext.Current, SessionManager.SessionFactory);
            if (session != null)
            {
                if (session.Transaction != null &&
                    session.Transaction.IsActive)
                {
                    session.Transaction.Rollback();
                }
                else
                    session.Flush();
                session.Close();
            }
        }

The reason for the rollback of open transaction in Application_EndRequest is to ensure that the transaction is not committed in case of an uncaught exception.

Finally we have to configure NHibernate to use the ManagedWebSessionContext, by setting the parameter current_session_context_class. There is a short name that can be used, “managed_web”. In your web.config file you add the following property:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
        ...
        <property name="current_session_context_class">
            managed_web
        </property>
        ...
    </session-factory>
</hibernate-configuration>

Now the property SessionManager.CurrentSession holds a reference to an open NHibernate session on every HTTP request. The usage is illustrated in the example code snippet below:

    ISession session = SessionManager.CurrentSession;
    session.BeginTransaction();
    Issue issue = new Issue();
    issue.Heading = txtHeading.Text;
    int id = (int)session.Save(issue);
    session.Transaction.Commit();
    lblCreated.Text = "Created issue with id: " + id;

As you can see this code snippet is taken from a code-behind file. In a real application you would of course put the code related to NHibernate into a DAO or Repository class.

Conclusion

What I have showed in this post is just one way of handling NHibernate sessions in an ASP.NET application. Another option which I haven’t investigated yet is to use the promising framework NHibernate Burrow. Maybe I will dig into that in another blog post.

kick it on DotNetKicks.com

Deploying ASP.NET Web Applications using NAnt

We have started implementing Contiuous Integration at our company. We began with Cruise Control.NET but have decided to try with TeamCity instead. One thing that we would like to do is to deploy ASP.NET web applications as part of our Continuous Integration process. We use the Web Application Project template in Visual Studio. One problem that we faced was: How can we copy all the neccesary files to the web server? We didn’t want to specify the files explicitly nor did we want to copy all files with certain file name extensions. Those approaches seemed very error-prone.

The first approach we used with Cruise Control.NET was to use the MSBuild target _CopyWebApplication which is defined in Microsoft.WebApplication.targets. I was not very pleased with this solution, although it worked. My main concern that we didn’t have any control over the process. When we started with TeamCity it turned out that this approach was not working anymore. The _CopyWebApplication target copies all files needed for the web application to a directory called _PublishedWebSites\<web application project name>. For some reason (probably a good one), TeamCity mangles the name of the project file, with the effect that the name of the directory where the web application files were copied to had a different name than expected.

So I decided to try another approach. Since I like NAnt much more than MSBuild, I started to explore if it was possible to use NAnt for this task. The main problem was that the valuable information on which content files were part of the application was buried in the project file. To utilize this information I used an XSL transformation to transform parts of the project file (which is an MSBuild script) into a NAnt script.

We would like to select all the files where the Build Action property is set to Content. Those files are listed in the project file as a Content element, like in this example:

  <ItemGroup>
    <Content Include="Default.aspx" />

Hence they can be selected with the XPath expression: /Project/ItemGroup/Content.

What I wanted my XSLT script to do was to create a NAnt script with the following content:

  1. A fileset with all the content files included
  2. A target which copies the files in the fileset to some directory defined by a property.

This is what I came up with:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="msxsl"
    xmlns:s="http://schemas.microsoft.com/developer/
        msbuild/2003">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <project
            xmlns="http://nant.sf.net/release/0.85/nant.xsd"
            name="GeneratedFromMsbuild">
            <fileset id="content.files"
                basedir="${{project.root}}">
                <xsl:for-each select=
                    "/s:Project/s:ItemGroup/s:Content">
                    <include name="{@Include}"/>
                </xsl:for-each>
            </fileset>
            <target name="copy.content">
                <copy todir="${{destination.dir}}">
                    <fileset refid="content.files"/>
                </copy>
            </target>
        </project>
    </xsl:template>
</xsl:stylesheet>

The following excerpt from our main NAnt script shows how the XLS transformation is invoked and how the resulting script is used to copy the content files:

<target name="deploy.web" depends="generatenant">
    <nant buildfile="${generated.file}" target="copy.content">
        <properties>
            <property name="project.root"
                value="${web.projectrootdir}"/>
            <property name="destination.dir"
                value="${web.rootdir}"/>
        </properties>
    </nant>
   <copy
       todir="${web.rootdir}\bin">
       <fileset basedir="${web.projectrootdir}\bin">
            <include name ="*.*"/>
       </fileset>
   </copy>
</target>

<target name="generatenant">
    <delete file="${generated.file}" failonerror="false"/>
    <style style="GenerateNantFromMSBuild.xslt"
        in="${web.projectrootdir}/${web.projectfilename}"
        out="${generated.file}"/>
</target>

Closing remarks

Of course there are several other ways of accomplishing this, like using a Web Deployment Project, but I personally like having full control of what is going on and that can be achieved with NAnt.