Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate Contextual Sessions in ASP.NET MVC

I want to use NHibernate's Contextual Sessions in my ASP.NET MVC 2 application, but I'm having a hard time finding guidance on how to properly do this.

I'm interested in Session per request.

like image 297
Bigglesby Avatar asked Sep 09 '10 19:09

Bigglesby


2 Answers

Please let me know if I am doing this the right way. Here is what I came up with:

Global.asax

public class MvcApplication : NinjectHttpApplication
{
    public MvcApplication()
    {
        NHibernateProfiler.Initialize();
        EndRequest += delegate { NHibernateHelper.EndContextSession(Kernel.Get<ISessionFactory>()); };            
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.IgnoreRoute("favicon.ico");

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
    }

    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }   

    protected override IKernel CreateKernel()
    {
        StandardKernel kernel = new StandardKernel();
        kernel.Load(AppDomain.CurrentDomain.GetAssemblies());            
        return kernel;
    }
}

NHibernateHelper

public class NHibernateHelper
{
    public static ISessionFactory CreateSessionFactory()
    {
        var nhConfig = new Configuration();
        nhConfig.Configure();

        return Fluently.Configure(nhConfig)
            .Mappings(m =>
                m.FluentMappings.AddFromAssemblyOf<Reservation>()
                .Conventions.Add(ForeignKey.EndsWith("Id")))
#if DEBUG
            .ExposeConfiguration(cfg =>
            {
                new SchemaExport(cfg)
                    .SetOutputFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "schema.sql"))
                    .Create(true, false);
            })
#endif
            .BuildSessionFactory();
    }


    public static ISession GetSession(ISessionFactory sessionFactory)
    {
        ISession session;
        if (CurrentSessionContext.HasBind(sessionFactory))
        {
            session = sessionFactory.GetCurrentSession();
        }
        else
        {
            session = sessionFactory.OpenSession();
            CurrentSessionContext.Bind(session);
        }
        return session;
    }

    public static void EndContextSession(ISessionFactory sessionFactory)
    {
        var session = CurrentSessionContext.Unbind(sessionFactory);
        if (session != null && session.IsOpen)
        {
            try
            {
                if (session.Transaction != null && session.Transaction.IsActive)
                {
                    // an unhandled exception has occurred and no db commit should be made
                    session.Transaction.Rollback();
                }
            }
            finally
            {
                session.Dispose();
            }
        }
    }
}

NHibernateModule

public class NHibernateModule : NinjectModule
{
    public override void Load()
    {
        Bind<ISessionFactory>().ToMethod(x => NHibernateHelper.CreateSessionFactory()).InSingletonScope();
        Bind<ISession>().ToMethod(x => NHibernateHelper.GetSession(Kernel.Get<ISessionFactory>()));
    }
}
like image 93
Bigglesby Avatar answered Oct 05 '22 23:10

Bigglesby


We do ours slightly differently to bigglesby, and I am not saying that his is wrong, or that ours is perfect.

In the global.asax we have on application start we have :

...
protected void Application_Start() {
    ISessionFactory sf =
        DataRepository
            .CreateSessionFactory(
                ConfigurationManager
                    .ConnectionStrings["conn_string"]
                    .ConnectionString
            );

//use windsor castle to inject the session 
ControllerBuilder
    .Current
    .SetControllerFactory(new WindsorControllerFactory(sf));
}
...

Our DataRepository we have: NOTE: (it is not a repository - my design error:bad name -, its more like your NHibernateHelper, I guess its more of a NH configuration wrapper of some sort...)

....
public static ISessionFactory CreateSessionFactory(string connectionString) {
   if (_sessionFactory == null){ 
    _sessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration ...
                    ... 
                    //custom configuration settings
                    ...
                    cfg.SetListener(ListenerType.PostInsert, new AuditListener());
                })
                .BuildSessionFactory();
    }
    return _sessionFactory;
}
....

The thing with the session factory, is you don't want to generate/build one on every request. The DataRepository acts as a singleton, insuring that the session factory is only created once, and that is on application start up. In our base controller, we inject either the session, or the sessionfactory into our controllers(Some controllers don't require a database connection, so they derive from a "database-less" base controller), using WindosrCastle. Our WindsorControllerFactory we have:

...
//constructor
public WindsorControllerFactory(ISessessionFactory) {
    Initialize();

    // Set the session Factory for NHibernate
    _container.Register(
    Component.For<ISessionFactory>()
             .UsingFactoryMethod(
                  () => sessionFactory)
                  .LifeStyle
                  .Transient
             );
}

private void Initialize() {
    _container = new WindsorContainer(
                     new XmlInterpreter(
                         new ConfigResource("castle")
                     )
                 );
    _container.AddFacility<FactorySupportFacility>();

    // Also register all the controller types as transient
    var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                      where typeof(IController).IsAssignableFrom(t)
                      select t;

    foreach (var t in controllerTypes) {
        _container.AddComponentLifeStyle(t.FullName, t, LifestyleType.Transient);
    }
}
....

With this set up, Every request generates a NHibernate session, and with our design we are also able to have controllers that do not generate sessions. And that is currently how it works for us.

Can I also say that I have found NHProf very useful when trying to configure or debug problem we have had.

like image 35
Dai Bok Avatar answered Oct 06 '22 01:10

Dai Bok