Fork me on GitHub

Setter Injection Edit on GitHub


StructureMap can inject dependencies into public setter properties as part of its construction process using the Setter Injection form of Dependency Injection. The StructureMap team strongly recommends using constructor injection wherever possible instead of setter injection. That being said, there are few cases where setter injection is probably easier (inheritance hierarchies), not to mention legacy or third party tools that simply cannot support constructor injection cough ASP.Net cough.

See this discussion from Martin Fowler on Constructor vs Setter Injection.

If you are having any trouble with setter injection in your StructureMap usage, make sure you're familiar with using Build Plans to help in troubleshooting

Explicit Setter Injection with [SetterProperty] Attributes

The simplest conceptual way to force StructureMap into making public setters mandatory service dependencies by decorating setter properties with the [SetterProperty] attribute like this example:


public class Repository
{
    private IDataProvider _provider;

    // Adding the SetterProperty to a setter directs
    // StructureMap to use this property when
    // constructing a Repository instance
    [SetterProperty]
    public IDataProvider Provider
    {
        set { _provider = value; }
    }

    [SetterProperty]
    public bool ShouldCache { get; set; }
}

Without the [SetterProperty] attributes decorating the setters, StructureMap would ignore the Provider and ShouldCache properties when it builds up a Repository object. With the attributes, StructureMap will try to build and attach values for the two properties as part of object construction.

If you were to look at StructureMap's "build plan" for the Repository class, you would see something like this:

PluginType: StructureMap.Testing.DocumentationExamples.Repository
Lifecycle: Transient
new Repository()
    Set IDataProvider Provider = **Default**
    Set Boolean ShouldCache = Value: False

If you intensely dislike runaway attribute usage, that's okay because there are other ways to enable setter injection in StructureMap.

Inline Setter Configuration

Any setter property not configured with [SetterProperty] or the setter policies in the next section can still be filled by StructureMap if an inline dependency is configured matching that setter property as shown in the example below:


public class RuleWithSetters : IEventRule
{
    public ICondition Condition { get; set; }
    public IAction Action { get; set; }

    public void ProcessEvent(SomeEvent @event)
    {
        if (Condition.Matches(@event))
        {
            Action.PerformWork(@event);
        }
    }
}

public class RuleWithSettersRegistry : Registry
{
    public RuleWithSettersRegistry()
    {
        For<IEventRule>().Use<RuleWithSetters>()
            .Setter<ICondition>().Is<Condition1>()

            // or
            .Setter(x => x.Action).Is(new Action1())

            // or if you need to specify the name
            .Setter<IAction>("Action").Is<Action2>()

            // or you can configure values *after* the object
            // is constructed with the SetProperty method
            .SetProperty(x => x.Action = new Action2());
    }
}

See also:

Setter Injection Policies

Lastly, you can give StructureMap some criteria for determining which setters should be mandatory dependencies with the Registry.Policies.SetAllProperties() method during Container setup as shown in this example below:


public class ClassWithNamedProperties
{
    public int Age { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public IGateway Gateway { get; set; }
    public IService Service { get; set; }
}

[Fact]
public void specify_setter_policy_and_construct_an_object()
{
    var theService = new ColorService("red");

    var container = new Container(x =>
    {
        x.For<IService>().Use(theService);
        x.For<IGateway>().Use<DefaultGateway>();

        x.ForConcreteType<ClassWithNamedProperties>().Configure.Setter<int>().Is(5);

        x.Policies.SetAllProperties(
            policy => policy.WithAnyTypeFromNamespace("StructureMap.Testing.Widget3"));
    });

    var description = container.Model.For<ClassWithNamedProperties>().Default.DescribeBuildPlan();
    Debug.WriteLine(description);

    var target = container.GetInstance<ClassWithNamedProperties>();
    target.Service.ShouldBeTheSameAs(theService);
    target.Gateway.ShouldBeOfType<DefaultGateway>();
}

All calls to Registry.Policies.SetAllProperties() are additive, meaning you can use as many criteria as possible for setter injection.

Filling Setter's of an Object

You can run into situations where you'll want StructureMap to fill the setter dependencies of an object that is built outside of your own code and cannot be built by StructureMap itself. Action filter attributes from ASP.Net MVC are an obvious example.

You're still in luck because StructureMap has the BuildUp(object) method for just this scenario as shown in this example from the unit tests:


public class BuildUpTarget1
{
    public IGateway Gateway { get; set; }
    public IService Service { get; set; }
}

[Fact]
public void create_a_setter_rule_and_see_it_applied_in_BuildUp()
{
    var theGateway = new DefaultGateway();
    var container = new Container(x =>
    {
        x.For<IGateway>().Use(theGateway);
        x.Policies.SetAllProperties(y => y.OfType<IGateway>());
    });

    var target = new BuildUpTarget1();
    container.BuildUp(target);

    target.Gateway.ShouldBeTheSameAs(theGateway);
    target.Service.ShouldBeNull();
}

The normal rules for what setters should be filled as described above apply to BuildUp().