I had a twitter conversation a while back with Paul Hammant about different ways to use a IoC container. Paul is one of the leads on PicoContainer, one of the Java containers. It all started with him coming across the Common Service Locator, which is an adapter to allow frameworks to leverage a container without forcing consumers to use a specific one. He had a big problem with one part of the implementation, the ServiceLocator class. This class is a static gateway to the container, and he pointed out this is the antithesis of dependency injection and inversion of control. While this is true, there has to be at least one place (usual a few places) in your application that gets access to the container to inject everything else.
In asp.net, this is usually achieved by storing your container in the ApplicationState dictionary, and then accessing it where it is needed. This pattern ends up being recreated in every application that uses a container, so container authors hid this functionality behind a static gateway. Is this a bad thing? If used as intended, I don’t see much real difference between using a global dictionary and using a static gateway (in fact, you could argue that the gateway is cleaner). Paul’s point; however, was that giving developers access to this static gateway will lead to it being used all sorts of places it shouldn’t. In practice, I have seen applications where is happens to some extent, but in many cases it has more to do with the lack of DI support in asp.net.
Paul proposed a way to control access to the container using packages in java. I came up with something similar in .net, although I had to use a separate assembly to enforce separation. For asp.net mvc, it boils down to putting your controller factory and other extension points in their own assembly, and wrapping the container to make the various resolution methods internal. That way, they can only be used in this infrastructure assembly. I am not crazy about having to wrap the container, but it’s not that much code:
public class ContainerWrapper { public const string ContainerKey = "Container"; Container container; public void InitializeContainer() { container = new Container(); container.Configure(c => c.Scan(s => { s.WithDefaultConventions(); s.AddAllTypesOf<IController>(); })); HttpContext.Current.Application[ContainerKey] = this; } internal object GetInstance(Type type) { return container.GetInstance(type); } internal T GetInstance<T>() { return container.GetInstance<T>(); } }
This is example uses StructureMap, and is very simplistic. In a real application you would probably want to scan for registries that would contain the registration logic (those would be in you main web assembly). The ControllerFactory looks this:
public class StructureMapControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType) { var application = requestContext.HttpContext.Application; var container = (ContainerWrapper)application[ContainerWrapper.ContainerKey]; return container.GetInstance(controllerType) as IController; } }
A complete sample project showing this off is on Github.
So I’m curious about two things. First, in your experience, do developers abuse service locators when they could be using dependency injection? Second, would it help to hide the container in an infrastructure assembly, only exposing a the registration and base classes that access the container internally?