18

Does anyone have any tips or best practices regarding how Autofac can help manage the NHibernate ISession Instance (in the case of an ASP.NET MVC application)?

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
UpTheCreek
  • 31,444
  • 34
  • 152
  • 221

2 Answers2

21

I'm not overly familiar with how NHibernate sessions should be handled. That said, Autofac have excellent instance lifetime handling (scoping and deterministic disposal). Some related resources are this article and this question. Since you're in ASP.Net MVC land make sure you also look into the MVC integration stuff.

To illustrate the point, here's a quick sample on how you can use Autofac factory delegates and the Owned generic to get full control over instance lifetime:

public class SomeController
{
    private readonly Func<Owned<ISession>> _sessionFactory;

    public SomeController(Func<Owned<ISession>> sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void DoSomeWork()
    {
        using (var session = _sessionFactory())
        {
             var transaction = session.Value.BeginTransaction();
             ....   
        }
    }
}

The container setup to get this to work is quite simple. Notice that we don't have to do anything to get the Func<> and Owned<> types, these are made available automatically by Autofac:

builder.Register(c => cfg.BuildSessionFactory())
    .As<ISessionFactory>()
    .SingleInstance();
builder.Register(c => c.Resolve<ISessionFactory>().OpenSession());

Update: my reasoning here is that, according to this NHibernate tutorial, the lifetime of the session instance should be that of the "unit of work". Thus we need some way of controlling both when the session instance is created and when the session is disposed.

With Autofac we get this control by requesting a Func<> instead of the type directly. Not using Func<> would require that the session instance be created upfront before the controller instance is created.

Next, the default in Autofac is that instances have the lifetime of their container. Since we know that we need the power to dispose this instance as soon as the unit of work is done, we request an Owned instance. Disposing the owned instance will in this case immediately dispose the underlying session.

Community
  • 1
  • 1
Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
  • 3
    Hmm... I wish people would give some constructive criticism when they downvote. – Peter Lillevold May 09 '10 at 10:18
  • Me too! :) Thanks for your answer - I'm taking a look into your suggestions – UpTheCreek May 14 '10 at 12:27
  • Hi, I understand everything here appart from the Func> - Why use this? Why not just inject into the standard ISession? I'm not sure I really understand what the func/Owned does, but according to the autofac documentation, if you use it you have to dispose of the object manually? A component that gains references to Owned is responsible for calling Owned.Dispose when the instance is no longer required. It is a bug to not clean up owned instances. http://code.google.com/p/autofac/wiki/NewInV2 – UpTheCreek May 18 '10 at 12:16
  • @UpTheCreek - see my update. Hopefully it clarifies my idea :) – Peter Lillevold May 18 '10 at 13:13
  • 2
    An alternate to Func> is to use the InstancePerHttpRequest for the ISession object. I believe its part of Autofac.Integration.Mvc (use nuget) – Chris Klepeis Oct 14 '11 at 21:18
  • @ChrisKlepeis - yes, you can do that, *if* your Unit-of-Work matches the scope of the request. That is often the case, but not always. – Peter Lillevold Oct 03 '12 at 20:33
  • How is this approach better than simply keeping the ISessionFactory in the container and doing Resolve().OpenSession() as needed? Works the same and does not require you to couple your interfaces to Autofac classes such as "Owned". – KT. Aug 18 '16 at 12:07
  • I am also not sure why would one need an Owned here. We are creating it ourselves, not via the container. Why would the container care about it at all? – KT. Aug 18 '16 at 12:09
0

Edit: Sounds like Autofac and probably other containers can scope the lifetime correctly. If that's the case, go for it.

It isn't a good idea to use your IoC container to manage sessions directly. The lifetime of your session should correspond to your unit of work (transaction boundary). In the case of a web application, that should almost certainly be the lifetime of a web request.

The most common way to achieve this is with an HttpModule that both creates your session and starts your transaction when a request begins, then commits when the request has finished. I would have the HttpModule register the session in the HttpContext.Items collection.

In your IoC container, you could register something like HttpContextSessionLocator against ISessionLocator.

I should mention that your generic error handling should locate the current session and roll back the transaction automatically, or you could end up committing half a unit of work.

James L
  • 16,456
  • 10
  • 53
  • 70
  • 1
    Agree with James on this, I inject a unit of work factory into my (asp.net mvc) controllers and create a unit of work from inside the controller when required. The factory is just wrapper class (service locator) around the IoC container (Structure Map) – AwkwardCoder May 08 '10 at 16:50
  • @AWC: thats exactly what I would do too. Thing is that the Autofac container gives you the factory stuff OOTB. – Peter Lillevold May 08 '10 at 17:25
  • 1
    Hi James, I believe that AutoFac provides HttpRequestScoped(), which I think will provide the same behaviour as the http module. I might be wrong, but I think one benefit is that this will only new up the object in requests that actually used it, rather than on all requests as you would get with the HttpModule. – UpTheCreek May 18 '10 at 12:21
  • Thanks for the tip re generic error handling and rolling back - will bear in mind! – UpTheCreek May 18 '10 at 12:22
  • 1
    @UpTheCreek. Interesting - I've never used Autofac before. It sounds like that'll work, as long is it calls Dispose for you. – James L May 18 '10 at 13:21