Fork me on GitHub

Constructor Selection Edit on GitHub


StructureMap 3.* greatly improves the control over the selection of constructor functions to build concrete types by allowing users to override the constructor selection on specific Instance's and register custom rules for selecting constructors to override the basic StructureMap behavior.

Greediest Constructor

If there are multiple public constructor functions on a concrete class, StructureMap's default behavior is to select the "greediest" constructor, i.e., the constructor function with the most parameters. In the case of two or more constructor functions with the same number of parameters StructureMap will simply take the first constructor encountered in that subset of constructors.

The default constructor selection is demonstrated below:


public class GreaterThanRule : Rule
{
    public string Attribute { get; set; }
    public int Value { get; set; }

    public GreaterThanRule()
    {
    }

    public GreaterThanRule(string attribute, int value)
    {
        Attribute = attribute;
        Value = value;
    }

    public GreaterThanRule(IWidget widget, Rule rule)
    {
    }
}

[Fact]
public void using_the_greediest_ctor()
{
    var container = new Container(_ =>
    {
        _.ForConcreteType<GreaterThanRule>().Configure
            .Ctor<string>("attribute").Is("foo")
            .Ctor<int>("value").Is(42);
    });

    var rule = container.GetInstance<GreaterThanRule>();
    rule.Attribute.ShouldBe("foo");
    rule.Value.ShouldBe(42);
}

New in StructureMap 4.0, the "greediest constructor selection" will bypass any constructor function that requires "simple" arguments like strings, numbers, or enumeration values that are not explicitly configured for the instance.

You can see this behavior shown below:


public class DbContext
{
    public string ConnectionString { get; set; }

    public DbContext(string connectionString)
    {
        ConnectionString = connectionString;
    }

    public DbContext() : this("default value")
    {
    }
}

[Fact]
public void should_bypass_ctor_with_unresolvable_simple_args()
{
    var container = new Container();
    container.GetInstance<DbContext>()
        .ConnectionString.ShouldBe("default value");
}

[Fact]
public void should_use_greediest_ctor_that_has_all_of_simple_dependencies()
{
    var container = new Container(_ =>
    {
        _.ForConcreteType<DbContext>().Configure
            .Ctor<string>("connectionString").Is("not the default");
    });

    container.GetInstance<DbContext>()
        .ConnectionString.ShouldBe("not the default");
}

Explicitly Selecting a Constructor

To override the constructor selection explicitly on a case by case basis, you can use the SelectConstructor(Expression) method in the Registry DSL as shown below:


public class Thingie
{
    public Thingie(IWidget widget)
    {
        CorrectCtorWasUsed = true;
    }

    public bool CorrectCtorWasUsed { get; set; }

    public Thingie(IWidget widget, IService service)
    {
        Assert.True(false, "I should not have been called");
    }
}

[Fact]
public void override_the_constructor_selection()
{
    var container = new Container(_ =>
    {
        _.For<IWidget>().Use<AWidget>();

        _.ForConcreteType<Thingie>().Configure

            // StructureMap parses the expression passed
            // into the method below to determine the
            // constructor
            .SelectConstructor(() => new Thingie(null));
    });

    container.GetInstance<Thingie>()
        .CorrectCtorWasUsed
        .ShouldBeTrue();
}

[DefaultConstructor] Attribute

Alternatively, you can override the choice of constructor function by using the older [DefaultConstructor] attribute like this:


public class AttributedThing
{
    // Normally the greediest ctor would be
    // selected, but using this attribute
    // will overrid that behavior
    [DefaultConstructor]
    public AttributedThing(IWidget widget)
    {
        CorrectCtorWasUsed = true;
    }

    public bool CorrectCtorWasUsed { get; set; }

    public AttributedThing(IWidget widget, IService service)
    {
        Assert.True(false, "I should not have been called");
    }
}

[Fact]
public void select_constructor_by_attribute()
{
    var container = new Container(_ => { _.For<IWidget>().Use<AWidget>(); });

    container.GetInstance<AttributedThing>()
        .CorrectCtorWasUsed
        .ShouldBeTrue();
}

Custom Constructor Selection Rule

If the constructor selection logic isn't what you want, you can systematically override the selection rules by registering one or more custom IConstructorSelector policies to a Container. Do note that you can register more than one policy and they will be executed from last one registered to the first one registered.

Let's say that you want to control the constructor selection for all concrete subclasses of an abstract class like this hiearchy:


public abstract class BaseThing
{
    public BaseThing(IWidget widget)
    {
        CorrectCtorWasUsed = true;
    }

    public bool CorrectCtorWasUsed { get; set; }

    public BaseThing(IWidget widget, IService service)
    {
        Assert.True(false, "I should not have been called");
    }
}

public class Thing1 : BaseThing
{
    public Thing1(IWidget widget) : base(widget)
    {
    }

    public Thing1(IWidget widget, IService service) : base(widget, service)
    {
    }
}

public class Thing2 : BaseThing
{
    public Thing2(IWidget widget) : base(widget)
    {
    }

    public Thing2(IWidget widget, IService service) : base(widget, service)
    {
    }
}

You could create a custom IConstructorSelector like this one below to override the constructor behavior for only subclasses of BaseThing:


public class ThingCtorRule : IConstructorSelector
{
    public ConstructorInfo Find(Type pluggedType, DependencyCollection dependencies, PluginGraph graph)
    {
        // if this rule does not apply to the pluggedType,
        // just return null to denote "not applicable"
        if (!pluggedType.CanBeCastTo<BaseThing>()) return null;

        return pluggedType.GetConstructor(new[] { typeof(IWidget) });
    }
}

Finally, you can register your custom rule as shown below:


[Fact]
public void use_a_custom_constructor_selector()
{
    var container = new Container(_ =>
    {
        _.For<IWidget>().Use<AWidget>();

        _.Policies.ConstructorSelector<ThingCtorRule>();
    });

    container.GetInstance<Thing1>()
        .CorrectCtorWasUsed.ShouldBeTrue();

    container.GetInstance<Thing2>()
        .CorrectCtorWasUsed.ShouldBeTrue();
}