Dependency Injection in ASP.NET MVC: Contextual controller injection

In the last post, we looked at the basic DI usage in ASP.NET MVC – instantiating controllers.  However, there are quite a few other things we can do with controllers besides merely instantiate them.  One thing to keep in mind with the Controller base class is that although you can inject your controller’s dependencies, quite a few others are around via properties.

For example, the IActionInvoker and ITempDataProvider are two replaceable services the base Controller class uses to do its work, but to replace these items with your own behavior you usually need to rely on either an inheritance hierarchy or doing service location in a custom controller factory.

So why would you want to replace these items?  For one, the IActionInvoker is basically the entire action execution pipeline, with all the filter usage and what not.  If you wanted to extend this pipeline or change it, you’d want to replace the IActionInvoker (or extend the default ControllerActionInvoker).

I don’t like this all hard-wired in to the controller factory, because hey, that’s what a container is for!

Property injecting the remaining dependencies

If we want to supply the property-injected dependencies to our controllers, we first need to configure the container to supply the correct instances.  With StructureMap, that’s pretty straightforward in our custom registry:

For<IActionInvoker>().Use<ControllerActionInvoker>();
For<ITempDataProvider>().Use<SessionStateTempDataProvider>();
For<RouteCollection>().Use(RouteTable.Routes);

SetAllProperties(c =>
{
    c.OfType<IActionInvoker>();
    c.OfType<ITempDataProvider>();
});

We first set up the implementations with the For..Use syntax.  Next, we need to tell StructureMap to look for specific properties to inject, with the SetAllProperties method.  We only want to set members of these two types, all others we’ll leave alone.  The last For() method around the RouteCollection is important, we’ll use it in a bit for some of the various XyzHelper types.

With that configuration in place, we don’t have to touch our factories or our controllers to modify the property-injected services.  We only need to modify our container configuration (which is the whole point of inversion of control).

Nested containers for contextual objects

One of the more advanced features of modern IoC containers is the concept of nested containers.  Nested containers are built from an existing container, but can be configured independently so that you can configure items that exist only for the context of that container instance.  Because a nested container is built from an existing container, it inherits the configuration of the parent container.

With ASP.NET MVC, that means that many of the contextual items of a request can be configured to be injected for a controller’s services, as opposed to passed around everywhere.  For example, the ControllerContext object is almost ubiquitous in the execution of a controller, and is found just about everywhere.

Instead of passing it around constantly (whether it’s needed or not), we can instead take the contextual objects as a constructor dependency, and configure our nested container to supply the correct contextual objects.  So what kinds of things can we supply, on demand as needed?

  • RequestContext
  • HttpContextBase
  • ControllerContext

The last one is a bit tricky, as a ControllerContext is built from a Controller, so we can only supply a Func<ControllerContext>, to be used later.  It might seem like a stretch, but it can be a lot easier to deal with a lazy dependency than dragging around the controller context everywhere we go.

So let’s look at how we’d create our controllers now:

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    var nestedContainer = _container.GetNestedContainer();

    requestContext.HttpContext.Items[_nestedContainerKey] = nestedContainer;

    ControllerBase controllerBase = null;

    Func<ControllerContext> ctxtCtor = () => controllerBase == null ? null : controllerBase.ControllerContext;

    nestedContainer.Configure(cfg =>
    {
        cfg.For<RequestContext>().Use(requestContext);
        cfg.For<HttpContextBase>().Use(requestContext.HttpContext);
        cfg.For<Func<ControllerContext>>().Use(ctxtCtor);
    });

    var controller = (IController)nestedContainer.GetInstance(controllerType);

    controllerBase = controller as ControllerBase;

    return controller;
}

We first create a nested container from the container passed in to our controller factory.  Next, we stick the nested container instance into the HttpContext items, as we’ll need to dispose the container when we release the controller later.  We build up a Func<ControllerContext> that supplies the ControllerContext from what’s built up from the ControllerBase class.  Because we don’t have a good way to override that piece, I left it alone.

On the other side, we want to configure the ReleaseController method to dispose of our nested container:

public override void ReleaseController(IController controller)
{
    var controllerBase = controller as Controller;

    if (controllerBase != null)
    {
        var httpContextBase = controllerBase.ControllerContext.HttpContext;

        var nestedContainer = (IContainer)httpContextBase.Items[_nestedContainerKey];

        if (nestedContainer != null)
            nestedContainer.Dispose();
    }

    base.ReleaseController(controller);
}

The only change we need to do is pull the nested container our from the controller’s context items.  The only strange piece I ran in to here is that the GetControllerInstance accepts a RequestContext, but I only have an IController to work with in the ReleaseController method.  In any case, we pull out the nested container, and if it exists, dispose it.

Using contextual items

To use our contextual items, we only need to make sure our controller depends on a service that uses them:

public class HomeController : Controller
{
    private readonly IFooService _fooService;

    public HomeController(IFooService fooService)
    {
        _fooService = fooService;
    }

At construction time, HomeController is built with the nested container, which means all the contextual configuration rules will be used.  That means our FooService implementation (and anything else it uses) can use any contextual item now, as long as it’s exposed as a dependency:

private readonly RequestContext _requestContext;
private readonly HttpContextBase _httpContext;
private readonly UrlHelper _helper;
private readonly Func<ControllerContext> _context;

public FooService(
    RequestContext requestContext,
    HttpContextBase httpContext,
    UrlHelper helper,
    Func<ControllerContext> context
    )
{
    _requestContext = requestContext;
    _httpContext = httpContext;
    _helper = helper;
    _context = context;
}

Note that third item.  Because I’ve configured both RequestContext and RouteCollection, StructureMap is now able to build a UrlHelper as a dependency:

public UrlHelper(RequestContext requestContext, RouteCollection routeCollection) {
    if (requestContext == null) {
        throw new ArgumentNullException("requestContext");
    }
    if (routeCollection == null) {
        throw new ArgumentNullException("routeCollection");
    }
    RequestContext = requestContext;
    RouteCollection = routeCollection;
}

Having these request items around is nice, especially if you want to do things like generate URLs based on the current request context and routes, but don’t want to pass around the helper objects everywhere.  With a nested container, you can configure items in the container that are only resolved for that container instance, giving you the ability to depend on any one of the typical context objects created during a request.

This is a bit more advanced usage of a container, and you can begin to see why we can’t use things like Common Service Locator in this instance.  For a while, the differentiating factor in IoC containers was their registration abilities.  These days, even object resolution is different among containers, with things like nested/child containers, instance swapping, auto-factories, Lazy<T> and Owned<T> support, Func<T> support and so on.

Next, we’ll look at some of the other items the Controller class new’s up and completely remove the service location/opaque dependencies inherent in the design of the ControllerBase class.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in ASP.NET MVC, Dependency Injection. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Luca Milan

    Hi Jimmy,

    you can use this pattern for inject a UnitOfWork into controller and our related services?

    thx!

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Luca

    Yep, you sure can. The only other issue is that many, many other services used by MVC are still all service-located or hard-coded, so your job becomes more difficult vs. other frameworks (like FubuMVC).

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Seems pretty involved. This is one of the driving reasons I created Siege.ServiceLocation … it contextually figures out which dependencies to inject an infinite level deep and you don’t have to track any of this stuff.

    If a dependency 5 levels deep is contextual and the rest are not, it resolves all with the default and the contextual dependency is correctly resolved. No need for code like this …

    Not intending to hijack, just pointing out a different approach.

  • Luca Milan

    Thx Jimmy.

    I could use this approach within a HTTP module (FubuMVC is much better!) between the beginning and end of the web request, so I guarantee the atomicity of the request.

  • jesus garza

    Hey Jimmy,

    Sorry if this is too newbie, but where do you get _nestedContainerKey from?

    Thanks.

  • http://www.maximeseguin.com Maxime Séguin

    Thank you for this nice article!

    Speaking of FubuMVC, would you suggest it over ASP.NET MVC if a project has carte blanche and why? I know it is more flexible right now but don’t you think that, in the end, a side project like FubuMVC would get less functionalities knowing the different resources (team size, money…)

    Maxime

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @jesus

    Whoops, just an oversight that I left that out:

    private static readonly object _nestedContainerKey = new object();

    Just a private field in the controller factory.

    @Maxime

    I’ve never developed/shipped an app with FubuMVC, but I have with MVC, so I can’t really talk about Fubu other than I really like what I see and I steal ideas on a regular basis. My suggestion would be to spike out a solution with Fubu and ASP.NET MVC 2, and see which one suits you better. After that, it’s really just up to you and your team’s comfort level on OSS.

  • Taber

    Im new to MVC and Structuremap and am implementing this concept to proivde the requestContext to other objects as needed.
    In my app I have many controllers deriving from one or more other controllers; I have tried but cant figure out a way to wireup IFooService on the baseController instead of all the inherited controllers.. is it even possible ??

  • jrnail23

    Hi Jimmy,
    I used this approach quite successfully, along with other examples you provided in this series on a recent project (MVC 2), and now that MVC 3 Preview 1 is upon us, can you provide some insight as to how you might use nested containers within their Service Locator approach?

    Brad Wilson suggests this (http://bradwilson.typepad.com/blog/2010/07/service-location-pt1-introduction.html#comment-6a00e54fbd8c498834013485c687f2970c):
    “depending on the context of the request, you could write a CSL implementation that is context aware and can automatically spin up nested containers based on the context. For example, if your strategy was to spin up a nested container per request, your CSL implementation could do that on your behalf, and work alongside an HTTP module which automatically cleaned up those containers when the request went out of scope. You’d still get your per-request containers while supporting the CSL.”

    Do you have any thoughts on how I might go about this with StructureMap, or if it’s even a desirable course of action in the first place?