NHibernate session management in the WCF Web API

Update! This post is still wrong (!!), and no longer relevant with the release of the ASP.NET MVC Web API code. I’ll provide my updated session handler soon.

 
As the WCF Web API project is a bit of a moving target at the moment, it’s been hard to try and find a sensible way to handle NHibernate sessions for each resource. They key word there is resource.

If you’ve ever used NHibernate with WCF before, you’ve probably organised your session management around the per-request model. That starts to break down with WCF Web APIs. You see, you have to consider how OData is consumed. This is where you have a resource operation that returns an IQueryable<T> that is evaluated after the operation returns. This is a really cool thing, but it breaks if you use a per-request style management method, or if you manually open and close the session for each operation. For example:

[ServiceContract]
public class MyResource
{

    private ISessionManager _sessionManager;
    private IWidgetRepository _widgetRepository;

    // You always inject, right?...
    public MyResource(ISessionManager s, IWidgetRepository r)
    {
        _sessionManager = s;
        _widgetRepository = r;
    }

    [WebGet(UriTemplate = "dtos")
    public IQueryable<MyDTO> GetAllMyDTOs()
    {
        using(_sessionManager.GetSession())
        {
            return _widgetRepository.Query();
        }
    }
}

This is bad/wrong/silly. As the response from GetAllMyDTOs isn’t actually evaluated until after the operation returns, it will fail, as you’ve disposed the session it’s linked to.

The other, “worserer” way is to just wait until the garbage collector cleans up your session by itself. I shouldn’t have to explain why that’s a monumentally stupid thing to do.

So, I had a bit of a think about this problem. While there is the option of message handlers and operation handlers, they still don’t give you a way of executing some code before and after an operation (to open and close the session). On top of that, operation handlers are asynchronous, so if you’re being good and binding your session to a ThreadStatic variable, it won’t be bound to the same thread your operation will actually execute on.

If you’re using dependency injection to build your resources (which you should), then you’ve no doubt used the WebApiConfiguration class and created a method on your class to handle the CreateInstance delegate. This method would locate the requested type in your DI container and return the composed instance.

Well, there’s a corresponding delegate exposed by WebApiConfiguration called ReleaseInstance. It’s job is to take an instance and get rid of it.

Now we have two methods, fired at each end of a resource’s life-cycle. The only concern I had is if, somehow, the create/release methods ran on a different thread to the operation. So I stuck a little logging of the thread ID in there and did a couple of tests. Same thread ID all around! Not only that, each subsequent quasi-simultaneous (because I can’t think of a better way to describe it) resource request utilised a different thread. At least, that’s the anecdotal evidence I’ve… anecdoted. So each resource would have it’s own session, as each session is bound to a ThreadStatic variable!

So, at the moment, I have a naïve implementation of session management, scoped per resource, that looks like this:

public class MyWebApi : System.Web.HttpApplication
{
    public void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }

    public void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathinfo}");

        var config = new WebApiConfiguration
                            {
                                EnableTestClient = true,
                                CreateInstance = Create,
                                ReleaseInstance = Release
                            };
        routes.SetDefaultHttpConfiguration(config);

        routes.MapServiceRoute<MyResource>("myresourceuri");
    }

    private object Create(Type t, InstanceContext c, HttpRequestMessage r)
    {
        var sm = Injector.Default.Get<ISessionManager>();
        sm.OpenSession();
        return Injector.Default.Get<t>();
    }

    private void Release(InstanceContext c, object i)
    {
        var sm = Injector.Default.Get<ISessionManager>();
        sm.CloseSession();
    }
}
Note: enabling the test client is fine, for testing. Make sure you disable it when you release, otherwise nasty people with bad haircuts and no friends will use it as an instructional guide to ruining your API.

 

As I said, this is a very simplified chunk of code. I haven’t load tested it sufficiently (read: at all) to be able to say with any Jobsian sense of confidence that it’s flawless. But it gives me the result I want at the moment. Until I find something better…