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:
// This is the wrong way to use an IoC container. Do NOT invoke the container from
// the constructor function. This tightly couples the ShippingScreenPresenter to
// the IoC container in a harmful way. This class cannot be used in either
// production or testing without a valid IoC configuration. Plus, you're writing more
// code
public ShippingScreenPresenter(IContainer container)
{
// It's even worse if you use a static facade to retrieve
// a service locator!
_service = container.GetInstance<IShippingService>();
_repository = container.GetInstance<IRepository>();
}
Instead of binding ShippingScreenPresenter
so tightly to StructureMap and having to explicitly fetch its dependencies, let's switch
it to using StructureMap a little more idiomatically and just exposing a constructor function with the necessary dependencies
as arguments:
// This is the way to write a Constructor Function with an IoC tool
// Let the IoC container "inject" services from outside, and keep
// ShippingScreenPresenter ignorant of the IoC infrastructure
public ShippingScreenPresenter(IShippingService service, IRepository repository)
{
_service = service;
_repository = repository;
}
As long as a StructureMap Container
knows how to resolve the IRepository
and
IShippingService
interfaces, StructureMap can build ShippingScreenPresenter
by using "auto-wiring." All this means is that
instead of forcing you to explicitly configure all the dependencies for ShippingScreenPresenter
, StructureMap can infer from
the public constructor function and public property setters
what dependencies ShippingScreenPresenter
needs and uses the defaults of both to build it out.
Looking at the build plan for ShippingScreenPresenter
:
public void ShowBuildPlan()
{
var container = new Container(_ =>
{
_.For<IShippingService>().Use<InternalShippingService>();
_.For<IRepository>().Use<SimpleRepository>();
});
// Just proving that we can build ShippingScreenPresenter;)
container.GetInstance<ShippingScreenPresenter>().ShouldNotBeNull();
var buildPlan = container.Model.For<ShippingScreenPresenter>().Default.DescribeBuildPlan(1);
Debug.WriteLine(buildPlan);
}
give us:
PluginType: StructureMap.Testing.DocumentationExamples.ShippingScreenPresenter Lifecycle: Transient new ShippingScreenPresenter(IShippingService, IRepository) ┣ IShippingService = **Default** ┃ | PluginType: StructureMap.Testing.DocumentationExamples.IShippingService ┃ | Lifecycle: Transient ┃ | new InternalShippingService() ┃ ┗ IRepository = **Default** | PluginType: StructureMap.Testing.DocumentationExamples.IRepository | Lifecycle: Transient | new SimpleRepository()
See Working with Primitive Types for more information on how StructureMap deals with primitive types like numbers, strings, enums, and dates in auto-wiring.