Fork me on GitHub

Fallback Services Edit on GitHub


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>();
}