The following is a technique that was stolen from FubuMVC where we used
the idea of default or "fallback" registrations to make it mechanically simple for the core framework to declare
the default service registrations in the Container
for what FubuMVC needed while allowing applications to happily
override specific registrations without having to worry about the order in which the registrations were done.
To see this in practice, say you have an application that will support client specific modularity that might allow
business clients to override the base StructureMap registrations. This is a perfect use case for
defining the application defaults with UseIfNone()
as shown in this example below:
public class DefaultServices : Registry
{
public DefaultServices()
{
// If nobody else provides a default
// for IWidget, use AWidget
For<IWidget>().UseIfNone<AWidget>();
}
}
public class SpecificServices : Registry
{
public SpecificServices()
{
// Use BWidget for IWidget, period
For<IWidget>().Use<BWidget>();
}
}
In application usage, you would add the default UseIfNone()
registrations, and optionally pick
up additional extension Registry
objects from extension assemblies as shown in this example:
[AttributeUsage(AttributeTargets.Assembly)]
public class ProductModuleAttribute : Attribute
{
}
public class ApplicationRegistry : Registry
{
public ApplicationRegistry()
{
// Use the default services as fallbacks
IncludeRegistry<DefaultServices>();
// Dependending on what assemblies are present,
// this might find specific registrations that
// will take precedence over the UseIfNone()
// registrations in DefaultServices
Scan(_ =>
{
_.AssembliesFromApplicationBaseDirectory(
assem => assem.HasAttribute<ProductModuleAttribute>());
_.LookForRegistries();
});
}
}
[Fact]
public void see_use_if_none_in_action()
{
var container1 = Container.For<DefaultServices>();
// No other registrations, so fallback
// to AWidget
container1.GetInstance<IWidget>()
.ShouldBeOfType<AWidget>();
var container2 = new Container(_ =>
{
// add both registries above
// NOTE: the order does not matter for IWidget
_.IncludeRegistry<SpecificServices>();
_.IncludeRegistry<DefaultServices>();
});
// The registration in SpecificServices
// should win out
container2.GetInstance<IWidget>()
.ShouldBeOfType<BWidget>();
}