October 6, 2012

Per-Request dependencies in ASP.NET Web API using StructureMap

In an effort to write useful code, not infrastructure, I avoided using an IoC container in my Web API application until I felt it would actually be a benefit. I’m now at a point where I need to share some resources “per request”, something that an IoC container can take care of very easily.

Since my IoC container/DI tool of choice is StructureMap, the first thing I did was install the WebApiContrib.IoC.StructureMap package from NuGet.

To wire it up in your application:

public static void Register(HttpConfiguration config)
{
    ObjectFactory.Initialize(cfg =>
    {
        // your config
    });

    config.DependencyResolver = new StructureMapResolver(ObjectFactory.Container);
}

What I wanted to do was share the same database session (in my case RavenDB’s IDocumentSession) per request. In an ASP.NET MVC application I would just configure StructureMap like so:

cfg.ForSingletonOf<IDocumentStore>().Use(InitializeStore());
cfg.For<IDocumentSession>().HttpContextScoped()
    .Use(ctx => ctx.GetInstance<IDocumentStore>().OpenSession());

This configures IDocumentStore as a singleton and opens a new session per HTTP request. The main reason I want to do this is so that I can commit all of the changes made within that session as one unit of work. Typically I would do this with a custom Action Filter.

I could do exactly the same thing in ASP.NET Web API but would make me dependent on HTTP causing my in-memory hosted integration tests to fail.

Fortunately ASP.NET Web API introduced IDependencyScope that allows us to control sharing of resources on a per-API-call basis.

Most of the implementations of IDependencyScope I have seen create a nested/child container that is used to resolve dependencies within the dependency scope.

With StructureMap, any dependencies configured as transient, will be scoped as Singleton for the lifetime of the nested container. So if a nested container is created per-request, those instances are shared per-request. When the nested container is disposed, StructureMap will automatically dispose any instances that implement IDisposable.

Since the StructureMap implementation from WebApiContrib uses a nested container I expected that everything would just “work”. I created the following Action Filter to save my database changes at the end of a request:

public class RavenDbUnitOfWorkAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var session = actionExecutedContext.Request.GetDependencyScope()
            .GetService(typeof(IDocumentSession)) as IDocumentSession;

        if (session != null && actionExecutedContext.Exception == null)
        {
            session.SaveChanges();
        }
        
        base.OnActionExecuted(actionExecutedContext);
    }
}

Unfortunately after making a few requests I realized that none of my changes were being saved. The reason for this was that my Action Filter was getting a different instance of IDocumentSession from that of my controllers.

My big assumption was that since controllers are created per-request, the framework would be creating them within a dependency scope, invoking IDependencyScope.BeginScope() at the beginning of the request.

However, the framework never calls BeginScope(). By default it will create the controller using the global dependency resolver (assuming you’re using one).

It is down to you to ensure that controllers are created within a dependency scope. The way to do this is to implement IHttpControllerActivator. There is a great explanation of this on Mark Seemann’s blog.

Those of you have seen the source code of the WebApiContrib StructureMapResolver are probably aware that it does implement IHttpControllerActivator.

However, if you look at the code:

public IDependencyScope BeginScope()
{
    return new StructureMapDependencyScope(container.GetNestedContainer());
}

public IHttpController Create(
	HttpRequestMessage request, 
	HttpControllerDescriptor controllerDescriptor, 
	Type controllerType)
{
    return container.GetNestedContainer()
		.GetInstance(controllerType) as IHttpController;
}

What this actually does is use one nested container for creating a controller and another when we start a new dependency scope (which of course is never called).

To resolve this, we can create the controller within the same scope:

public IHttpController Create(
    HttpRequestMessage request,
    HttpControllerDescriptor controllerDescriptor,
    Type controllerType)
{
    var scope = request.GetDependencyScope();
    return scope.GetService(controllerType) as IHttpController;
}

Now our controller dependencies are shared per request so the IDocumentSession injected into my controllers is the same instance as that retrieved by my Action Filter.

I’ll be sending a pull request to WebApiContrib to hopefully fix this issue. For now I’ve included a complete working example below:

public class StructureMapDependencyScope : IDependencyScope
{
    private IContainer container;

    public StructureMapDependencyScope(IContainer container)
    {
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        return serviceType.IsAbstract || serviceType.IsInterface
                 ? container.TryGetInstance(serviceType)
                 : container.GetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.GetAllInstances(serviceType).Cast<object>();
    }

    public void Dispose()
    {
        if (container != null)
        {
            container.Dispose();
            container = null;
        }
    }
}

public class StructureMapDependencyResolver :
    StructureMapDependencyScope, IDependencyResolver, IHttpControllerActivator
{
    private readonly IContainer container;

    public StructureMapDependencyResolver(IContainer container)
        : base(container)
    {
        this.container = container;
        container.Inject<IHttpControllerActivator>(this);
    }

    public IDependencyScope BeginScope()
    {
        return new StructureMapDependencyScope(container.GetNestedContainer());
    }

    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        var scope = request.GetDependencyScope();
        return scope.GetService(controllerType) as IHttpController;
    }
}

© 2022 Ben Foster