views:

466

answers:

3

I've raised this question before but am still struggling to find an example that I can get my head around (please don't just tell me to look at the S#arp Architecture project without at least some directions).

So far I have achieved near persistance ignorance in my web project. My repository classes (in my data project) take an ISession in the constructor:

public class ProductRepository : IProductRepository
{
    private ISession _session;
    public ProductRepository(ISession session) {
        _session = session;
    }

In my global.asax I expose the current session and am creating and disposing session on beginrequest and endrequest (this is where I have the dependency on NHibernate):

    public static ISessionFactory SessionFactory = CreateSessionFactory();

    private static ISessionFactory CreateSessionFactory() {
        return new Configuration() 
            .Configure()
            .BuildSessionFactory();
    }

    protected MvcApplication()  {
        BeginRequest += delegate {
            CurrentSessionContext.Bind(SessionFactory.OpenSession());
        };
        EndRequest += delegate {
            CurrentSessionContext.Unbind(SessionFactory).Dispose();
        };
    }

And finally my StructureMap registry:

    public AppRegistry() {
        For<ISession>().TheDefault
            .Is.ConstructedBy(x => MvcApplication.SessionFactory.GetCurrentSession());

        For<IProductRepository>().Use<ProductRepository>();
    }

It would seem I need my own generic implementations of ISession and ISessionFactory that I can use in my web project and inject into my repositories?

So just to clarify - I am using NHibernate in my repository layer and want to use a session-per-(http)request. Therefore I am injecting an ISession into my repository constructors (using structuremap). Currently to create and dispose the sessions in each request I have had to reference NHibernate from my web project. This is the dependency I would like to remove.

Thanks, Ben

+2  A: 

Why don't you create an IHttpModule and perform your creation and disposing there (probably in the Begin_Request and End_Request events), but put your IHttpModule inside the project that has your NHibernate dependency. eg.

namespace MyWebApp.Repository.NHibernateImpl
{
    public class NHibernateModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(Context_BeginRequest);
            context.EndRequest += new EventHandler(Context_EndRequest);
        }

        private void Context_BeginRequest(object sender, EventArgs e)
        {
            // Create your ISession
        }

        private void Context_EndRequest(object sender, EventArgs e)
        {
            // Close/Dispose your ISession
        }

        public void Dispose()
        {
            // Perhaps dispose of your ISessionFactory here
        }
    }
}

There is maybe a better way, I'm interested to know this as well, so any alternative suggestions?

Sunday Ironfoot
@SundayIronfoot - this is the route I started going down but like you I'm interested if there is a better (more DI friendly) way of doing this.
Ben
@Ben - But it isn't a bad bad dependency as you will not have to refer to this objects from yours neither reference it in your web project. All you have to do is to configure that in your web.config. What's the problem? If you were configuring your DI container via XML you would have to type it too.
jfneis
@jfneis - have you got a complete example of using a HttpModule - I am particularly interested in how I can still inject my session into repositories and whether transactions are handled any differently.
Ben
@Ben - Billy McCafferty's popular article shows how to handle txs with HttpModules: http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx.
jfneis
A: 

Thanks for everyone's help so far. A bit more research led me to the NHibernate Burrow project.

From the project FAQ (http://nhforge.org/wikis/burrow/faq.aspx):

Burrow is a light weight middleware developed to support .Net applications using NHibernate (maybe also referred as NH in this article) as ORM framework. Using Asp.net with NHibernate could be a challenge because of the fact that NHibernate is a stateful environment while Asp.net is a stateless framework. Burrow can help solve this conflict by providing advanced and smart session/transaction management and other facilitates.

I had to jump through a few hoops to get it working in my project. Since the current release uses an old version of NHibernate I had to download the latest source from the trunk, open in VS, add references to latest version of NHibernate and recompile (fortunately no errors).

I tested NHibernate Burrow in a number of ways.

1) Continue injecting ISession into my repositories

To do this, I had to add references to NHibernate, NHibernate.Burrow and NHibernate.Burrow.WebUtil to my MVC project.

In web.config I had to set up Burrow (see http://nhforge.org/wikis/burrow/get-started.aspx) and then in my StructureMap registry add the following:

       For<ISession>()
            .TheDefault.Is
            .ConstructedBy(x => new NHibernate.Burrow.BurrowFramework().GetSession());          

I like this approach as it means my repositories (or controllers) are not coupled with Burrow. I don't really like the fact that I have to reference these three assemblies in my web project but at least I lose the code for managing the session - this is all handled by Burrow.

2) The second approach would be to set ISession in my repository constructors like so:

    public ProductRepository() : 
        this(new BurrowFramework().GetSession()) { }

    public ProductRepository(ISession session) {
        _session = session;
    }

I can still override ISession making my repositories testable. I then have a direct dependency on Burrow but perhaps this isn't such a bad thing?

On the plus side, the only assembly I need to reference from my web project is NHibernate.Burrow.WebUtils.

Interested to see which of the two people would go for and why.

Ben
+1  A: 

In my opinion, you should embrace the ISession and work with it directly. The problem with many session-per-request implementations is that they delay committing database changes until the end of the HTTP request. If the transaction fails, all you can do at that point is direct the user to a generic error page. It's much better to manage the transaction on the page so that you can catch and handle errors more effectively. If you take this route then you need to access the ISession or a wrapper to control the transaction.

Also, at some point your application will probably need to use properties or methods exposed by ISession, especially Merge and Load.

Jamie Ide