7

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

Ben Foster
  • 34,340
  • 40
  • 176
  • 285
  • 6
    NHibernate is according to me not a bad dependency. – Will Marcouiller May 26 '10 at 12:43
  • My web project talks to the repository and NHibernate is a dependency of the repository. If it wasn't for the fact that I wanted session-per-request then I could just open my sessions inside the repository layer. However, I do want session-per-request but I don't want my web project to tied to NHibernate. – Ben Foster May 26 '10 at 13:25
  • 2
    Will Marcouiller: It's still a dependency. A dependency is a dependency. What if Ben wanted to port his app over to CouchDB or MongoDB or use an in-memory object database? – Sunday Ironfoot May 26 '10 at 14:22
  • 3
    @Sunday Ironfoot: if he had to change from a RDBMS to CouchDB, MongoDB or anything else so different from a RDBMS he'd probably have to rethink the entire architecture and this dependency would be the least of his problems. – Mauricio Scheffer May 26 '10 at 23:51
  • 2
    @Mauricio I strongly disagree, I can't see any reason why you couldn't continue to use a DDD (Domain Driven Design) approach with something like MongoDB (including using existing entities), after all C# still remains an Object-Oriented programming language even if you use a NoSQL approach. A well abstracted/decoupled Repository layer means all you think about is calling methods on that repository which returns collections of objects, thoughts on RDBMS/NoSQL/flatXML files etc. shouldn't even come into it. Have a look at NoRM http://github.com/atheken/NoRM provides LINQ to MongoDB support. – Sunday Ironfoot May 27 '10 at 07:30
  • @Ben - What did you finally use? How did you manage this? – Jack7 Mar 21 '12 at 15:08
  • @Jack7 we use StructureMap to scan for registries in external assemblies. Details of my complete implementation can be found at http://ben.onfabrik.com/posts/yet-another-session-per-request-post – Ben Foster Mar 21 '12 at 16:42

3 Answers3

3

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
  • 12,840
  • 15
  • 75
  • 91
  • 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 Foster May 26 '10 at 14:40
  • @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 May 26 '10 at 19:13
  • @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 Foster May 27 '10 at 09:31
  • @Ben - Billy McCafferty's popular article shows how to handle txs with HttpModules: http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx. – jfneis May 30 '10 at 23:18
2

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
  • 48,427
  • 16
  • 81
  • 117
  • What would you propose instead of the session per request pattern? Or are you suggesting to not open a session and transaction at the begging and then close them at the end of the request? – RandomWebGuy Sep 22 '11 at 22:30
0

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 Foster
  • 34,340
  • 40
  • 176
  • 285