Wednesday, March 20, 2013

Unity and Http Per Request Lifetime Manager

A common requirement for an IoC container is to support different lifetime managers with at least the “transient” and “container controlled” provided out-of-the-box. Unity is no exception and lifetime management works great.

Another, more specific but still common requirement is to have a “per-request” lifetime manager so that the container would resolve the same the instance of your service in a http request scope but different scopes would yield different instances of the service. This way for example, the container could serve database sessions with no risk of unintented concurrency issues.

The small inconvenience of Unity is that the “per request” manager is not provided and you have to bake one on your own. The aim of this post is to warn the reader that although this sounds like an easy task, one can easily find an incorrent implementation and get into trouble.

Let us take these two implementations and compare them:

  1. Using Microsoft Unity in ASP.NET MVC by Brian Mains
  2. Unity and lifetime management by John Bledsoe

First one:

public class HttpContextLifetimeManager<T> : LifetimeManager, IDisposable
{
    private HttpContextBase _context = null;
    public HttpContextLifetimeManager()
    {
        _context = new HttpContextWrapper(HttpContext.Current);
    }
    public HttpContextLifetimeManager(HttpContextBase context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        _context = context;
    }
    public void Dispose()
    {
        this.RemoveValue();
    }
    public override object GetValue()
    {
        return _context.Items[typeof(T)];
    }
    public override void RemoveValue()
    {
        _context.Items.Remove(typeof(T));
    }
    public override void SetValue(object newValue)
    {
        _context.Items[typeof(T)] = newValue;
    }
}

The second one:

public class PerRequestLifetimeManager : LifetimeManager
{
    private readonly object key = new object();
 
    public override object GetValue()
    {
        if (HttpContext.Current != null && 
            HttpContext.Current.Items.Contains(key))
            return HttpContext.Current.Items[key];
        else
            return null;
    }
 
    public override void RemoveValue()
    {
        if (HttpContext.Current != null)
            HttpContext.Current.Items.Remove(key);
    }
 
    public override void SetValue(object newValue)
    {
        if (HttpContext.Current != null)
            HttpContext.Current.Items[key] = newValue;
    }
}

Seemingly both are correct as both use the Items container which is request-specific which means that items are stored to and retrieved from the request specific context. So far, so good.

However, there is a fundamental problem in the first implementation. The problem comes from the fact that the lifetime manager has its own lifetime management policy – the manager is created once for a container and registered type.

In a web application, typically you have a single, shared container in the http application scope. Following the Composition Root principle, you initialize the container and your services somewhere early in the application’s life cycle but the container is alive and is reused when there is a need to resolve services in the application scope.

In the first approach, the lifetime manager is initialized once and it always uses the http context of the very first request, storing the context in a private variable and reusing it in subsequent requests. This, of course, is completely against the requirement to always use the current request context!

Lesson learned – pay a close attention to the lifetime management policy and how it is implemented.

2 comments:

HIREN DESAI said...

I imeplemented the second approach and now when I try executing code, connections are never closed. They go in sleeping mode. I have implemented UnitOfWork, Repository Pattern. DBContext & UnitOfWork are kept PerHttpRequestLifeTimeManager.

Deeps said...

We are faci ng some problems when using the first one.
Thanks for the artile ..