Which DI container/framework should I choose?
None; just roll your own and use simple Service Location
I'm not meaning to ruffle any feathers, but I couldn't come up with a good case on the spot, but as I've thought about it some more, I completely disagree, respectfully (at least for the reasons that I choose to use a DI container).
What are the reasons I use to decide whether or not to use a DI container?
- Reduce Coupling
- Declarative Configuration
- Rapid Development
Using a simple service locator pattern, with a hand-rolled instantiation mechanism, can get you the first two, but not the last (unless you spend many many hours on your hand-rolled solution, which would most likely turn out just like one of the already existing containers). If you don't care about declarative configuration, does that mean the using a container vs not using a container is roughly the same? No.
There are two other major benefits that I get from using a container that are not in the list above, but I feel are important, and help testability.
- Published/declared dependencies
Published dependencies vs implicit dependencies
Below is an illustration of two constructors. The first uses published dependencies, and the latter uses implicit.
public MovieLister( IMovieFinder finder )
// DI style
_finder = finder;
// service locator style
_finder = ServiceLocator.Resolve<IMovieFinder>("CsvMovieFinder", "movies.txt");
The first is more easily testable and usable, because you know your dependencies up front (published by the constructor signature). Whereas you don't know unless you look at the implementation which services are needed with the implicit dependency example. Additionally, with this example, you're tied to the implementation of the service locator, and also to using the CsvMovieFinder key (I suppose this key could be in AppConfig or some other configuration mechanism, so it could actually be configurable).
I agree it can be just as simple to inject mocks into your service locator for testing the implicit example, but I'd rather not have to think about it. Publishing those dependencies up front makes it easier for the client consuming that object to use and extend, because then the client developer knows exactly what is expected.
When dependencies are published, it allows the container to use autowiring to build up your application instance. This may sound like hand-waving magic to those of you that haven't seen it in action, but it truly is one of the best features that comes from using a DI container.
The following example shows a simple example of how autowiring can work.
public class Program
private IContainer _container;
// configure container
_container = new Container();
public void Run()
// uses autowiring to inject CsvMovieFinder
// into MovieLister
MovieLister lister = _container.Resolve<MovieLister>();
lister.List( Console.Out );
The container builds up a dependency graph for the requested object, and walks it bottom-up, supplying each parent with the required dependencies. In this example, the container sees the published dependency that the MovieLister has on the IMovieFinder, and automatically instantiates the IMovieFinder it knows about and injects it as it is created. This case is somewhat trivial since the graph is only one or two levels deep, but I know you've ran into code before where this would be useful.
Service Location with a DI container
Sometimes it is useful to use the service locator pattern, I'm certainly not refuting that. Its definitely possible, and is usually the easiest way to get introduced to using a DI container actually. In fact, the last example uses the container in exactly that fashion when pulling the MovieLister out of the container. The program is using the container as the service location facility. The key being though, that we're leveraging the robustness of the container to autowire and instantiate anything else it needs rather than requiring each dependency to wire themselves up.
It is also worth mentioning that Microsoft has released a common container interface called the CommonServiceLocator, for helping framework developers abstract their container choice away from client developers, so they can pick whichever container (or roll their own) that they want.
Hopefully this clears something up for someone, and doesn't just muddy the waters.