Index
Let’s start at the beginning which is allegedly a very good place to start. Before StructureMap can construct any kind of concrete type for you it must be told what it should construct. StructureMap contains some powerful mechanisms to simplify this but to start with we’ll do it all manually. Later parts of this series will show how to apply conventions to configure StructureMap automatically.
Getting started with StructureMap is easy. First head over to http://structuremap.github.com/structuremap/ and download the latest release. Extract the StructureMap.dll assembly and add a reference to it in your project. You’re now ready to start using StructureMap in your application.
Pro tip: Remember to unblock the assembly if you’re on a version of windows that has this “feature” or you’ll get some very strange errors at runtime.
We can initialise StructureMap using the ObjectFactory class. This static class provides the primary interface for interacting with the StructureMap container. We can configure the container using either the Initialize or Configure methods. These methods provide very similar functionality. The primary difference is that Initialize will reset any existing configuration whereas Configure is additive to existing configuration (Initialize also provides some functions only relevant to the initial configuration of the container). In most cases Initialize is the appropriate method to call at the start of your application. Configure can then be used if you need to add to the configuration elsewhere.
So how does this work? Let’s take a simple example where I have some service SimpleService. In order to allow types that depend on this service to be independent of it’s implementation I create an abstraction in the form of the interface ISimpleService. These types look like this:
public interface ISimpleService
{
void DoStuff();
}
public class SimpleService : ISimpleService
{
public void DoStuff()
{
Console.WriteLine("Doing stuff");
}
}
If I was to request ISimpleService from StructureMap now I’d get a StructureMapException telling me there was no default instance registered for that type. This can be fixed with a call to Initialize like so:
ObjectFactory.Initialize(initialise =>
{
initialise.For<ISimpleService>().Use<SimpleService>();
});
This is a Nested Closer, a term you don’t need to entirely understand in order to use it. What is important to know is that StructureMap evaluates the lambda passed to this method in order to configure the container. StructureMap provides a Fluent Interface for configuration that is both very readable and highly expressive. Let’s examine this simple example.
We start by calling the For<T> method on initialise. The type parameter specifies the plugin type that we’re dealing with. The plugin type is the type we deal with when requesting instances from the container and is generally an abstraction (interface or abstract class).
The return type of the For<T> method provides a number of methods for configuring what we are registering. This is typical of fluent interfaces (which are outside the scope of this discussion). In this case we are using the simple no-parameter Use<T> method. The type parameter specifies what concrete class is to be supplied when someone requests the plugin type. Here we are specifying that SimpleService will be provided when ISimpleService is requested. Due to the magic of generics when we specify the configuration in this was we will get a compile time error if the concrete type does not implement or derive from the plugin type.
This is sufficient code to start requesting things, or at least instances of ISimpleService, from the container. This may be accomplished using the GetInstance<T> method:
var simpleService = ObjectFactory.GetInstance<ISimpleService>();
simpleService.DoStuff(); The simpleService variable will be of type ISimpleService. Running this code will print he message Doing stuff to the console. By itself this isn’t particularly useful but it is a building block on top of which we can get more sophisticated and useful functionality. Consider:
public interface IMoreComplexService
{
void DoMoreComplexStuff();
}
public class MoreComplexService : IMoreComplexService
{
private readonly ISimpleService _simpleService;
public MoreComplexService(ISimpleService simpleService)
{
_simpleService = simpleService;
}
public void DoMoreComplexStuff()
{
Console.WriteLine("Start complex stuff");
_simpleService.DoStuff();
Console.WriteLine("End complex stuff");
}
}
Here we have a more complex service that uses ISimpleService in order to perform its function. Rather than creating a concrete instance of its dependency through a traditional mechanism such as new or a factory method the MoreComplexService class requires it as a constructor parameter (I’ve omitted for clarity the null check production code should have). When creating an instance of MoreComplexService some implementation of ISimpleService must be provided. One of the nicest and most powerful features of StructureMap is that it can do this for us. This feature is called auto-wiring and it greatly simplifies composing your application. To make this all work all we need to do is tell StructureMap about the new IMoreComplexService, like so:
ObjectFactory .Initialize(initialise =>
{
initialise.For<ISimpleService>().Use<SimpleService>();
initialise.For<IMoreComplexService>().Use<MoreComplexService>();
});
With this configuration StructureMap has everything it needs to supply instances of IMoreComplexService. We can retrieve and use an instance of IMoreComplexService as shown:
var moreComplexService = ObjectFactory.GetInstance<IMoreComplexService>();
moreComplexService.DoMoreComplexStuff();
When IMoreComplexService is requested from the container the configuration indicates that MoreComplexService is the concrete type to be supplied. However this type does not have a no-argument constructor meaning that more information is required to create an instance. Using reflection the container can determine that what is required is an instance of ISimpleService, a type StructureMap knows how to construct. It will the perform auto-wiring, automatically creating the necessary dependency and supplying (or wiring) them where required.
The MoreComplexService here is using Constructor Injection where the necessary dependencies are injected via the constructor. This is generally the preferred way to inject dependencies as it ensures that they are available when the instance is used. There are however some exceptions to this which will be discussed later in this series.
Provided that there are no circular dependencies the container is capable of wiring dependencies to any level. These elements are enough to fulfil many common applications for dependency injection. However most production applications will need more control over the creation and lifecycle of some instances. Additionally having to specify each instance individually is verbose and somewhat error prone. Rectifying these concerns will be the subject of the next few posts in this series.