It took long enough, and I've had a pretty good stream of people asking for this, so here it is: StructureMap 2.0 is finally ready for download on Sourceforge. My goals with the release were to fully support Generic types, to create a much smoother user experience, and improve the ease of use. If you're not familiar with the twin concepts of Inversion of Control or Dependency Injection, there are some links at the bottom for background information.
I started work on StructureMap in the fall of 2002 as an attempt to learn .Net by writing the world's ultimate O/R Mapper. Unsurprisingly, that didn't work out very well, and I stopped my work on the O/R mapping shortly after ObjectSpaces was announced (sic). In the early months of 2004 I was working for ThoughtWorks, and PicoContainer in Java was all the rage on our internal boards. I picked back up StructureMap, harvested the overly elaborate configuration subsystem I had been building, and in far too late nights in a terminal in Chicago O'Hare waiting for my delayed flight home, turned StructureMap into a tool for doing Dependency Injection and Service Location in .Net. StructureMap was first used in a production system in June of 2004, and I've used it since on all but one short project. This is the 4th release.
Here's just a taste of the new capabilities in StructureMap 2.0, with a lot more to follow over the next couple weeks. If you've been intrigued by ObjectBuilder's potential but put off by its limitations, or you're completely aggravated by the string keys of a certain competing tool, check out StructureMap for a smoother experience and much more power.
Generics
The biggest new feature is full support for doing dependency injection and service location with generic types, including n-deep auto wiring. Say you have two classes and interfaces (elided) like this:
public interface IDataService<T>
{
T Load(int id);
void Save(T target);
}
public class DataService<T> : IDataService<T>
{
}
public interface IRepository<T>
{
T Find(int id);
void Save(T target);
}
public class Repository<T> : IRepository<T>
{
private readonly IDataService<T> _service;
// Repository requires an instance of IDataService
public Repository(IDataService<T> service)
{
_service = service;
}
}
The configuration for these generic types is:
<StructureMap>
<DefaultInstance
PluginType="StructureMap.Testing.IDataService`1, StructureMap.Testing"
PluggedType="StructureMap.Testing.DataService`1, StructureMap.Testing" />
<DefaultInstance
PluginType="StructureMap.Testing.IRepository`1, StructureMap.Testing"
PluggedType="StructureMap.Testing.Repository`1, StructureMap.Testing" />
</StructureMap>
In this configuration we've just specified the default concrete types to be used for both IDataService<T> and IRepository<T>. If either class had any primitive constructor arguments like database connection strings, you would also have to configure property values for these values. Note the class name "Repository`1." We're specifically setting up the templated type in memory. StructureMap will spot that this class is a templated type and keep back a list of internal InstanceFactory objects for each templated class. In usage, you would retrieve a Repository like this:
IRepository<Invoice> repository =
ObjectFactory.GetInstance<IRepository<Invoice>>();
When this line is called for the first time, StructureMap takes the cached InstanceFactory for IRepository<>, and uses that as a prototype to create and attach a new InstanceFactory for IRepository. Please note that StructureMap supports "auto wiring." In this example, StructureMap knows the default instance to return for a type of IDataService. When the default IRepository is requested, StructureMap is going to return a concrete Repository object. Looking at the constructor for Repository there is an argument for an IDataService object. Unless directed otherwise, StructureMap will push in the default IDataService into that constructor argument.
P.S. Am I the only person that thinks sprinkling generics all over makes for ugly code?
Easier Configuration
One of the goals for this release was to make the Xml configuration easier to use and reduce the "signal to noise ratio." The underlying model of PluginFamily/Plugin/InstanceMemento is completely unchanged, but I've strived to hide more of the complexity in the configuration files. Say you only need to define the default concrete type for a given interface, and maybe configure a couple of constructor arguments along the way. Here's a sample of new capabilities in the configuration schema.
<StructureMap MementoStyle="Attribute">
<DefaultInstance
PluginType="StructureMap.Testing.Widget.IWidget,StructureMap.Testing.Widget"
PluggedType="StructureMap.Testing.Widget.ColorWidget,StructureMap.Testing.Widget"
Color="Red" />
<DefaultInstance
PluginType="StructureMap.Testing.Widget.Rule,StructureMap.Testing.Widget"
PluggedType="StructureMap.Testing.Widget.ColorRule,StructureMap.Testing.Widget"
Color="Blue"
Scope="Singleton"
Name="TheBlueOne"/>
</StructureMap>
First, in the <StructureMap> node you'll see a new attribute called MementoStyle. In the last release I introduced a new terse, attribute normalized mode to configure instance properties. By setting MementoStyle to "Attribute," I'm directing StructureMap to use the attribute normalized style throughout. The new <DefaultInstance> node is a shorthand way just to specify the default instance of a targeted PluginType in one node. In the <DefaultInstance> nodes above, I'm specifying the targeted PluginType (IWidget), the actual concrete class to use (ColorWidget : IWidget), and setting the one constructor argument "Color." I can also specify the instance to be scoped as a singleton or another scoping choice.
Long time StructureMap users will notice that there are no <Assembly> nodes or <PluginFamily> nodes. In this example they are unnecessary.
Flexible Configuration
Besides generics, the next most common request has been for more flexible configuration choices besides the StructureMap.config file. And now that there is a somewhat justifiable backlash against programming in Xml, what if you don't want to use Xml configuration at all? There's a new sheriff in town, the StructureMapConfiguration class. In your application startup, make calls to the static methods on StructureMapConfiguration to direct StructureMap how and where to get its configuration.
// You know what, I don't want to use the StructureMap.config file
StructureMapConfiguration.UseDefaultStructureMapConfigFile = false;
// I want to pull configuration from these 3 files instead
StructureMapConfiguration.IncludeConfigurationFromFile("Component1.config");
StructureMapConfiguration.IncludeConfigurationFromFile("Component2.config");
StructureMapConfiguration.IncludeConfigurationFromFile("Component3.config");
// On startup, I want to check the configuration for any problems
// and run environment tests
StructureMapConfiguration.OnStartUp()
.FailOnException()
.WriteProblemsTo("problems.txt");
Programmatic Configuration with a Fluent Interface
StructureMap 2.0 includes a new Fluent Interface API as a programmatic mechanism for adding PluginFamily's and Plugin's. For simpler systems without a lot of variable configuration, or for the Xml averse, the new API may be the best way to go. The Fluent Interface is a superset of the configuration possible with attributes, and has the advantages of keeping classes clean of StructureMap intrusion and keeping the StructureMap configuration relatively contained in a cohesive area instead of being scattered throughout the code.
// Simply set up StructureMap to return a StubbedGateway class whenever IGateway is requested
StructureMapConfiguration.BuildInstancesOf<IGateway>().TheDefaultIsConcreteType<StubbedGateway>();
// Instead, set the default IGateway to the real thing, and also specify the host and port
// number. Pull the host and port number from AppSettings
StructureMapConfiguration.BuildInstancesOf<IGateway>()
.TheDefaultIs(
Registry.Instance<IGateway>().UsingConcreteType<ServerGateway>()
.WithProperty("host").EqualToAppSetting("ServerName")
.WithProperty("port").EqualToAppSetting("Port")
);
Information on Inversion of Control and Dependency Injection
Upcoming Tutorials and Samples
Watch my blog in the upcoming weeks for a stream of little tutorials and samples of using StructureMap, including some functionality that I don't believe is offered by other tools. The subjects I'm thinking about writing about are:
- Why use a Dependency Injection tool at all?
- The new Fluent Interface API for programmatic configuration
- Lessons Learned for building a Fluent Interface
- Using StructureMap with zero Xml
- An experience report on applying the DRY principle to StructureMap's configuration schema
- Configuration options for composite applications
- Configuration Management support in StructureMap
- Diagnostics and Environment test support in StructureMap
- Auto wiring support
- Best practices for using StructureMap in tests to inject mocks and stubs
- An example of using StructureMap to create a pluggable Chain of Responsibility
- User and machine specific configuration
- Dependency Injection of ASP.Net UserControl's
Please feel free to send any suggestions or comments to me.
Posted
04-02-2007 10:27 AM
by
Jeremy D. Miller