Most of the time you will be using StructureMap to build objects based on pre-canned configuration established upfront, but StructureMap
also has the capability to supply dependencies by type or named parameters (if you know the name of constructor arguments or setter property names) to the Container at runtime using the IContainer.With()
methods.
Why would you use this? Here are a few examples from my own usage over the years:
- In adhoc code, retrieve some service but override a
connectionString
constructor argument to connect to a different database - If you were to use StructureMap as a configuration intermediary, it becomes common to use this mechanism to swap out configuration on the fly
- Inject an Entity object into a service (uncommon now, but I did this on several systems years ago)
- Inject something contextual that can only be built at runtime like an ASP.Net HttpContext into a pre-configured object graph
Now, for some samples. Let's say that we have some classes like so:
public class ColorWidget : IWidget
{
public string Color { get; set; }
public ColorWidget(string color)
{
Color = color;
}
}
public class GuyWithWidgetAndService
{
public IWidget Widget { get; set; }
public IService Service { get; set; }
public GuyWithWidgetAndService(IWidget widget, IService service)
{
Widget = widget;
Service = service;
}
}
and unless stated otherwise in a sample, a Container
configured like so:
var container = new Container(x =>
{
x.For<IWidget>().Use<BWidget>();
x.For<IService>().Use<AService>();
});
Passing Named Arguments
The original usage of explicit arguments was to replace primitive arguments to constructor functions like this sample:
[Fact]
public void supply_named_arguments()
{
var container = new Container(x => { x.For<IWidget>().Use<ColorWidget>().Ctor<string>().Is("Red"); });
container.GetInstance<IWidget>()
.ShouldBeOfType<ColorWidget>()
.Color.ShouldBe("Red");
container.With("color").EqualTo("Blue")
.GetInstance<IWidget>()
.ShouldBeOfType<ColorWidget>()
.Color.ShouldBe("Blue");
}
The canonical usage is overriding file paths, database connection string, or urls.
Passing Arguments with a Fluent Interface
You can invoke the explicit argument passing as a fluent interface starting with
IContainer.With()
like the following sample:
var widget = new BWidget();
var service = new BService();
var guyWithWidgetAndService = container
.With<IWidget>(widget)
.With<IService>(service)
.GetInstance<GuyWithWidgetAndService>();
guyWithWidgetAndService
.Widget.ShouldBeTheSameAs(widget);
guyWithWidgetAndService
.Service.ShouldBeTheSameAs(service);
Using a Nested Closure
If you dislike fluent interfaces or want to pass in a lot of dependencies, the nested closure syntax might be more usable:
var widget = new BWidget();
var service = new BService();
var guyWithWidgetAndService = container
.With(x =>
{
x.With<IWidget>(widget);
x.With<IService>(service);
})
.GetInstance<GuyWithWidgetAndService>();
guyWithWidgetAndService
.Widget.ShouldBeTheSameAs(widget);
guyWithWidgetAndService
.Service.ShouldBeTheSameAs(service);
Using the ExplicitArguments object
Finally, you can also pass an object of type ExplicitArguments
directly to an overload
of the IContainer.GetInstance()
method:
var widget = new BWidget();
var service = new BService();
var args = new ExplicitArguments();
args.Set<IWidget>(widget);
args.Set<IService>(service);
var guyWithWidgetAndService = container
.GetInstance<GuyWithWidgetAndService>(args);
guyWithWidgetAndService
.Widget.ShouldBeTheSameAs(widget);
guyWithWidgetAndService
.Service.ShouldBeTheSameAs(service);