The best way to use an IoC container is to allow "Auto Wiring" to do most of the work for you. IoC Containers like StructureMap are an infrastructure concern, and as such, should be isolated from as much of your code as possible. Before examining Auto Wiring in depth, let's look at a common anti pattern of IoC usage:
One of the worst, but sadly most common, usages of an IoC container is shown below:
Typically, you’ll try to minimize the number of Service Locator (Container.Get*****) usages in your system to a bare minimum (I found 8 in my current system, but I think I’ll find a way to prune half of those later). Most of the value of an IoC tool is in automatically doing Dependency Injection. I’m working with the new MVC framework at the moment, so it’s a handy sample. Let’s say that we have a Controller class for a typical CRUD screen. That Controller class will generally need to interact with both validation services and the data access functionality of the Repository. Here’s a representative Controller class:
public class SomeScreenController : IController
private readonly IRepository _repository;
private readonly IValidator _validator;
// SomeScreenController depends on both IRepository and IValidator
public SomeScreenController(IRepository repository, IValidator validator)
_repository = repository;
_validator = validator;
So let’s get StructureMap set up for this SomeScreenController class:
// Set up the IValidator
// Set up the IRepository
You’ll notice that we didn’t make any explicit configuration for the SomeScreenController class, but yet we could now call:
var controller = ObjectFactory.GetInstance<SomeScreenController>();
and StructureMap will happily create a new instance of the SomeScreenController class by invoking its constructor and passing in a new Validator object and a new Repository object created with the connection string from the App.config file. We didn’t need to tell StructureMap how to construct SomeScreenController because:
This feature is known as “auto wiring,” and all the mainstream IoC containers support this feature to some extent or another.
By default, as long as an object is being created by invoking its constructor function, StructureMap will try to create/resolve/find an object for each non-primitive dependency in the requested concrete type. If StructureMap doesn't "know" how to find a requested dependency, it will throw an exception. By design, StructureMap cannot auto wire primitive arguments like strings and numbers. The Auto Wiring can be overriden by explicit configuration (this might actually be easier with Xml configuration):
In the example above, the IWidget dependency of the WidgetRule class is overriden.
Within a single object request, StructureMap will only create a single object for a single Instance configuration. What that means in effect is that if two or more objects in a single request need the same dependency, those two objects will get the exact same instance of that dependency. Let's immediately jump into code to demonstrate this.
This auto wiring policy was intended for objects that need to be shared by lots of other objects. A common example of this is some sort of DataContext class:
Now, let's say that I have a hierarchy of classes that all need to work on a DataContext:
When you request an object of Class3 with a call to Container.GetInstance<Class3>() like this:
The output is:
Class3 has Context: Id: 3abe0330-e94f-48a3-b8c3-56d278eea07f Class2 has Context: Id: 3abe0330-e94f-48a3-b8c3-56d278eea07f Class1 has Context: Id: 3abe0330-e94f-48a3-b8c3-56d278eea07f
In the sample above, when we write out the Class3, Class2, and Class1 objects to Debug.WriteLine, we find that each of these objects have a reference to the same DataContext. If we were to run this test again, the output might be:
Class3 has Context: Id: 109329ce-4058-4a35-9fd1-46d47c1e69e7 Class2 has Context: Id: 109329ce-4058-4a35-9fd1-46d47c1e69e7 Class1 has Context: Id: 109329ce-4058-4a35-9fd1-46d47c1e69e7
We see the exact same behavior, but it was a different object instance of DataContext for the new object graph.
This behavior also applies to objects passed in to the Container as an explicit argument:
The output of this unit test is:
The context being passed in is Id: 87ddccfd-a441-41fd-a86d-3f32987496ba Class3 has Context: Id: 87ddccfd-a441-41fd-a86d-3f32987496ba Class2 has Context: Id: 87ddccfd-a441-41fd-a86d-3f32987496ba Class1 has Context: Id: 87ddccfd-a441-41fd-a86d-3f32987496ba
The point of the sample above is just to show that the object instance of DataContext passed into the Container is used to create the Class3, Class2, and Class1 objects.
StructureMap has always supported Dependency Injection of arrays of dependency objects. New in StructureMap 2.5+ is a policy that if any array of dependencies is not explicitly specified, StructureMap will inject all possible instances of that dependency type. The sample below illustrates this auto wiring policy. I have a class called "ClassThatUsesValidators" that needs an array of IValidator objects. Below, I've configured two different Instances of ClassThatUsesValidator, one that explicitly configures its children IValidator and another Instance that is just going to let auto wiring inject the IValidator's.
The output of what_are_the_validators() is:
With Auto Wiring Name: Red Name: Blue Name: Purple Name: Green ================================= With Explicit Configuration Name: Red Name: Green