Fork me on GitHub

On Missing Family Policies Edit on GitHub


New for StructureMap 3.0 is a feature to create missing service registrations at runtime based on pluggable rules using the new IFamilyPolicy interface:



/// <summary>
/// Allows StructureMap to fill in missing registrations by unknown plugin types
/// at runtime
/// </summary>
public interface IFamilyPolicy
{
    /// <summary>
    /// Allows you to create missing registrations for an unknown plugin type
    /// at runtime.
    /// Return null if this policy does not apply to the given type
    /// </summary>
    PluginFamily Build(Type type);

    /// <summary>
    /// Should this policy be used to determine whether or not the Container has
    /// registrations for a plugin type in the PluginGraph.HasFamily(type) method
    /// </summary>
    bool AppliesToHasFamilyChecks { get; }
}

Internally, if you make a request to IContainer.GetInstance(type) for a type that the active Container does not recognize, StructureMap will next try to apply all the registered IFamilyPolicy policies to create a PluginFamily object for that plugin type that models the registrations for that plugin type, including the default, additional named instances, interceptors or decorators, and lifecycle rules.

The simplest built in example is the EnumerableFamilyPolicy shown below that can fill in requests for IList<T>, ICollection<T>, and T[] with a collection of all the known registrations of the type T:


public class EnumerableFamilyPolicy : IFamilyPolicy
{
    public PluginFamily Build(Type type)
    {
        if (EnumerableInstance.IsEnumerable(type))
        {
            var family = new PluginFamily(type);
            family.SetDefault(new AllPossibleInstance(type));

            return family;
        }

        return null;
    }

    public bool AppliesToHasFamilyChecks
    {
        get
        {
            return false;
        }
        
    }
}

The result of EnumerableFamilyPolicy in action is shown by the acceptance test below:


[Fact]
public void collection_types_are_all_possible_by_default()
{
    // NOTE that we do NOT make any explicit registration of
    // IList<IWidget>, IEnumerable<IWidget>, ICollection<IWidget>, or IWidget[]
    var container = new Container(_ =>
    {
        _.For<IWidget>().Add<AWidget>();
        _.For<IWidget>().Add<BWidget>();
        _.For<IWidget>().Add<CWidget>();
    });

    // IList<T>
    container.GetInstance<IList<IWidget>>()
        .Select(x => x.GetType())
        .ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));

    // ICollection<T>
    container.GetInstance<ICollection<IWidget>>()
        .Select(x => x.GetType())
        .ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));

    // Array of T
    container.GetInstance<IWidget[]>()
        .Select(x => x.GetType())
        .ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));
}


See also the Handling Missing Named Instances for runtime determination of named instances within a known plugin type.

Built In Policies

StructureMap and StructureMap.AutoMocking use several IFamilyPolicy rules internally to create default behavior. In all cases, any custom IFamilyPolicy rule that you explicitly add to a Container will be evaluated before the built in policies.

  1. CloseGenericFamilyPolicy - uses the registration for an open type as a template to create the registrations for a closed type the first time StructureMap encounters that closed type. See Generic Types for more information.
  2. FuncBuildByNamePolicy - Used internally to create registrations for Func&lt;string, T&gt; builders.
  3. EnumerableFamilyPolicy - Shown above.
  4. AutoMockedContainer - Used by StructureMap.AutoMocking to create registrations on the fly that just return a mock object for the requested plugin type.

Using a Custom IFamilyPolicy" id="custom">

FubuMVC 2.0 (still unreleased to the public as of yet, but in production usage) uses a custom family policy in its StructureMap integration to auto-resolve concrete configuration types like the following type:


public class SomeSettings
{
    public string ThisDirectory { get; set; }
    public string ThatDirectory { get; set; }
}


Unless the system using this object has explicitly registered SomeSettings, we want StructureMap to resolve this object by using data from the basic .Net appSettings collection to create a SomeSettings object.

For the sake of the example, assume that you have a functioning service that implements this interface below:


public interface ISettingsProvider
{
    T SettingsFor<T>() where T : class, new();

    object SettingsFor(Type settingsType);
}

public class AppSettingsProvider : ISettingsProvider
{
    public T SettingsFor<T>() where T : class, new()
    {
        return SettingsFor(typeof (T)).As<T>();
    }

    public object SettingsFor(Type settingsType)
    {
        // The real one reads key/value data from
        // the appSettings and uses FubuCore's
        // model binding to assign data to a new
        // object of settingsType
        return null;
    }
}


Assuming that ISettingsProvider is registered in your StructureMap Container, you could then craft a custom IFamilyPolicy class like this:


public class SettingPolicy : IFamilyPolicy
{
    public PluginFamily Build(Type type)
    {
        if (type.Name.EndsWith("Settings") && type.IsConcreteWithDefaultCtor())
        {
            var family = new PluginFamily(type);
            var instance = buildInstanceForType(type);
            family.SetDefault(instance);

            return family;
        }

        return null;
    }

    public bool AppliesToHasFamilyChecks
    {
        get { return true; }
    }


    private static Instance buildInstanceForType(Type type)
    {
        var instanceType = typeof (SettingsInstance<>).MakeGenericType(type);
        var instance = Activator.CreateInstance(instanceType).As<Instance>();
        return instance;
    }
}

// SettingsInstance just uses the registered service for ISettingsProvider to
// build the real object
public class SettingsInstance<T> : LambdaInstance<T> where T : class, new()
{
    public SettingsInstance() : base("Building {0} from application settings".ToFormat(typeof (T).FullName),
        c => c.GetInstance<ISettingsProvider>().SettingsFor<T>())
    {
    }
}


SettingPolicy is able to create a registration on the fly for any concrete type whose name ends in "Settings" and has a default, no arg constructor.

To use register the custom SettingPolicy, use one of the Registry.Policies.OnMissingFamily() methods:


public class SettingsRegistry : Registry
{
    public SettingsRegistry()
    {
        For<ISettingsProvider>().Use<AppSettingsProvider>();
        Policies.OnMissingFamily<SettingPolicy>();
    }
}


You can see the real implementation of the SettingPolicy in action in its integration tests on GitHub.