The ASP.NET Web API (beta) defines a class called HttpControllerContext. As the name implies, it provides a context for a Controller. This article describes how to inject an instance of this class into a Service.

The Problem

A Service may need an instance of the HttpControllerContext class. For an example, see the RouteLinker class in my previous post. A Controller, on the other hand, may depend on such a Service:

public CatalogController(IResourceLinker resourceLinker)

How can a CatalogController instance be wired up with an instance of RouteLinker, which again requires an instance of HttpControllerContext? In contrast to the existing ASP.NET MVC API, there's no easy way to read the current context. There's no HttpControllerContext.Current method or any other easy way (that I have found) to refer to an HttpControllerContext as part of the Composition Root.

True: it's easily available as a property on a Controller, but at the time of composition, there's no Controller instance (yet). A Controller instance is exactly what the Composition Root is attempting to create. This sounds like a circular reference problem. Fortunately, it's not.

The Solution

For Poor Man's DI, the solution is relatively simple. As I've previously described, by default the responsibility of creating Controller instances is handled by an instance of IHttpControllerActivator. This is, essentially, the Composition Root (at least for all Controllers).

The Create method of that interface takes exactly the HttpControllerContext required by RouteLinker - or, put differently: the framework will supply an instance every time it invokes the Create method. Thus, a custom IHttpControllerActivator solves the problem:

public class PoorMansCompositionRoot : IHttpControllerActivator
{
    public IHttpController Create(
        HttpControllerContext controllerContext,
        Type controllerType)
    {
        if (controllerType == typeof(CatalogController))
        {
            var url = HttpContext.Current.Request.Url;
            var baseUri =
                new UriBuilder(
                    url.Scheme,
                    url.Host,
                    url.Port).Uri;
 
            return new CatalogController(
                new RouteLinker(
                    baseUri,
                    controllerContext));
        }
 
        // Handle other types here...
    }
}

The controllerContext parameter is simply passed on to the RouteLinker constructor.

The only thing left is to register the PoorMansCompositionRoot with the ASP.NET Web API. This can be done in Global.asax by using the GlobalConfiguration.Configuration.ServiceResolver.SetResolver method, as described in my previous post. Just resolve IHttpControllerActivator to an instance of PoorMansCompositionRoot.


Comments

mick delaney
this is a nightmare really... has there been a discussion on the web api forums?
http://forums.asp.net/1246.aspx/1?Web+API
i think i might create one and reference this article directly if thats ok?

i upvoted your uservoice issue and there seems to be another one more generally on DI for asp.net http://aspnet.uservoice.com/forums/41199-general-asp-net/suggestions/487734-put-ioc-front-and-centre

doesn't seem like they listen to anyone though....


2012-04-20 09:35 UTC
Yes, please go ahead :)
2012-04-20 09:58 UTC
mick delaney
i've started it off from here.

will add some more examples to the thread over the weekend

http://forums.asp.net/p/1795175/4943052.aspx/1?p=True&t=634705120844896186
2012-04-20 13:49 UTC
It is an interesting problem.

I would also like to look at it differently. ControllerContext is a volatile object with its lifetime spanning only a single method. It is true that we typically limit the lifetime of a controller to a single method but it can be different.

One major difference here is that ControllerContext is not a classic dependency since it is added AFTER the object creation by the framework and not provided by the DI frameqork. As such I could resort to an Initialise method on my RouteLinker object, making it responsibility of controller to provide such volatile information.
2012-04-23 09:09 UTC
Ali, that's not an approach I like. First of all because an Initialize method would introduce a Temporal Coupling, but also because it would mean that one would potentially need to pass the HttpControllerContext through a lot of intermediary layers in order to invoke that Initialize method on a very low-level dependency. That's API pollution.
2012-04-23 09:24 UTC
Well, I know that you are an authority in the subject. So I probably need to go away, read your links and then have a think about it. Thanks anyway, and keep up the good work.
2012-04-23 16:32 UTC
I realize that this article is more general, but specifically for IResourceLinker, would it make more sense to just pass HttpControllerContext as a second parameter from controller action. What would be the downside of this approach?
2012-05-02 14:03 UTC
Dmitry, that would mean that you'd need to pass the parameter through each and every method through all intermediary layers (see also my answer above). This would seriously pollute the API.
2012-05-02 14:10 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Tuesday, 17 April 2012 15:17:05 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!