recommendation pattern to use for handling sessions per web requests in mvc using NHibernate

1.5k Views Asked by At

For example. My session factory is located in MyDomain.SessionProvider class. Session can be open using ISession session = SessionProvider.Instance.OpenSession()

Step: SessionProvider.cs

public static SessionProvider Instance { get; private set; }
        private static ISessionFactory _SessionFactory;

        static SessionProvider()
        {
            var provider = new SessionProvider();
            provider.Initialize();
            Instance = provider;
        }

        private SessionProvider()
        {

        }

        private void Initialize()
        {
            string csStringName = "ConnectionString";
            var cfg = Fluently.Configure()
               //ommiting mapping and db conf.

                .ExposeConfiguration(c => c.SetProperty("current_session_context_class", "web"))
                .BuildConfiguration();
            _SessionFactory = cfg.BuildSessionFactory();

        }

        public ISession OpenSession()
        {
            return _SessionFactory.OpenSession();
        }

        public ISession GetCurrentSession()
        {
            return _SessionFactory.GetCurrentSession();
        }

Step: Global.asax.cs

public static ISessionFactory SessionFactory { get; private set; }

Application Start

SessionFactory = SessionProvider.Instance.OpenSession().SessionFactory;

App_BeginRequest

var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);   

EndRequest dispose session

var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();

Step3.HomeController I should be using current session like

var session = SessionProvider.Instance.GetCurrentSession();
using (var tran = session.BeginTransaction())
{
   //retrieve data from session
}

Now, with trying to retrieve data on my controller like desc. in Step3. I got error message that my session is closed. I tried to remove Application_EndRequest block inside global.asax cause my transaction is wrapped with session but with no success. Still same error.

Second/side question: is this pattern accepted widely, or it is better to wrapped inside custom attributes on mvc controllers. Thanks.

Updated: On my controller when try to instantiate current session in line

var session = SessionProvider.Instance.GetCurrentSession();

I'm getting following error:

**Connection = 'session.Connection' threw an exception of type 'NHibernate.HibernateException'**

**base {System.ApplicationException} = {"Session is closed"}**
2

There are 2 best solutions below

1
On BEST ANSWER

Thanks @LeftyX

I solved this problem using TekPub video Mastering NHibernate with some customizations.

Global.asax

//Whenever the request from page comes in (single request for a page)
//open session and on request end close the session.

public static ISessionFactory SessionFactory =
   MyDomain.SessionProvider.CreateSessionFactory();

public MvcApplication() 
{
    this.BeginRequest += new EventHandler(MvcApplication_BeginRequest);
    this.EndRequest +=new EventHandler(MvcApplication_EndRequest);
}

private void MvcApplication_EndRequest(object sender, EventArgs e)
{
    CurrentSessionContext.Unbind(SessionFactory).Dispose();
}

private void MvcApplication_BeginRequest(object sender, EventArgs e)
{
    CurrentSessionContext.Bind(SessionFactory.OpenSession());
}

protected void Application_Start()
{
    SessionFactory.OpenSession();
}

and inside my controller

 var session = MvcApplication.SessionFactory.GetCurrentSession();
 {
     using (ITransaction tx = session.BeginTransaction())
      {... omitting retrieving data}
 }
0
On

You can find a couple of simple and easy implementations here and here and find some code here.
I like Ayende's approach to keep everything simple and clean:

public class Global: System.Web.HttpApplication
{
    public static ISessionFactory SessionFactory = CreateSessionFactory();

    protected static ISessionFactory CreateSessionFactory()
    {
        return new Configuration()
            .Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "hibernate.cfg.xml"))
            .BuildSessionFactory();
    }

    public static ISession CurrentSession
    {
        get{ return (ISession)HttpContext.Current.Items["current.session"]; }
        set { HttpContext.Current.Items["current.session"] = value; }
    }

    protected void Global()
    {
        BeginRequest += delegate
        {
            CurrentSession = SessionFactory.OpenSession();
        };
        EndRequest += delegate
        {
            if(CurrentSession != null)
                CurrentSession.Dispose();
        };
    }
}

In my projects I've decided to use a IoC container (StructureMap).
In case you're interested you can have a look here.