Fork me on GitHub

Working with IConfiguredInstance Edit on GitHub


The most common way for StructureMap to build or resolve a requested object is to build a concrete type directly by calling a public constructor function and optionally filling values in public setter properties. For this type of object construction, StructureMap exposes the IConfiguredInstance interface as a means of querying and modifying how a concrete type will be created or resolved. While the Registry DSL fluent interface provides the main way of explicitly configuring concrete type creation, the IConfiguredInstance interface is meant to support conventional registration, configuration attributes, and construction policies.


public interface IConfiguredInstance
{
    string Name { get; set; }

    Type PluggedType { get; }

    DependencyCollection Dependencies { get; }

    void AddInterceptor(IInterceptor interceptor);

    void SetLifecycleTo<T>() where T : ILifecycle, new();

    void SetLifecycleTo(ILifecycle lifecycle);

    ILifecycle Lifecycle { get; }

    ConstructorInfo Constructor { get; set; }

    bool HasBuildPlan();

    void ClearBuildPlan();
}

Changing the Instance Lifecycle

You can override the lifecycle of a single IConfiguredInstance by calling the LifecycleIs() methods and either supplying a type of ILifecycle or an ILifecycle object. As a quick helper, there are also extension methods for common lifecycles:


IConfiguredInstance instance
    = new ConfiguredInstance(typeof(WidgetHolder));

// Use the SingletonThing lifecycle
instance.Singleton();

// or supply an ILifecycle type
instance.SetLifecycleTo<ThreadLocalStorageLifecycle>();

// or supply an ILifecycle object
instance.SetLifecycleTo(new Lifecycles_Samples.MyCustomLifecycle());

// or override to the default "transient" lifecycle
instance.DefaultLifecycle();

Reflecting over Constructor Parameters

To find the constructor function parameters of an IConfiguredInstance, just use this syntax (it's just .Net Reflection):


public class GuyWithArguments
{
    public GuyWithArguments(IWidget widget, Rule rule)
    {
    }
}

[Fact]
public void reflecting_over_constructor_args()
{
    IConfiguredInstance instance = new SmartInstance<GuyWithArguments>()
        // I'm just forcing it to assign the constructor function
        .SelectConstructor(() => new GuyWithArguments(null, null));

    instance.Constructor.GetParameters().Select(x => x.Name)
        .ShouldHaveTheSameElementsAs("widget", "rule");
}

The constructor function selection process takes place as the very first step in creating a build plan and will be available in any kind of construction policy or configuration attribute on parameters or properties.

Reflecting over Setter Properties

There's a helper extension method off of `IConfiguredInstance' for finding all of the settable properties that StructureMap can work with as shown below:


public class GuyWithProperties
{
    public IWidget Widget { get; set; }
    public Rule Rule { get; private set; }
}

[Fact]
public void get_settable_properties()
{
    IConfiguredInstance instance
        = new ConfiguredInstance(typeof(GuyWithProperties));

    instance.SettableProperties()
        .Single().Name.ShouldBe("Widget");
}

Working with Dependencies

The IConfiguredInstance.Dependencies property is a collection of Argument objects that model inline dependencies. A single Argument can refer to a public property or the parameter in a constructor function and consists of:

  1. Type - the dependency type that would match a property or parameter argument
  2. Name - matches the name of a property or parameter argument
  3. Dependency - either an object or value of the dependency type or an Instance object that can be used to build the dependency

When StructureMap determines a build plan for a concrete type, it reflects over all the parameters in the chosen constructor function and then the settable properties looking for any explicitly configured dependencies by searching in order for:

  1. An exact match by dependency type and name
  2. A partial match by dependency type only
  3. A partial match by name only

For primitive arguments like strings or numbers, the logic is to search first by name, then by type. All searching is done in the order that the Argument objects are registered, so do watch the order in which you add arguments. There is a method to insert new arguments at the front of the list if you need to do any kind of overrides of previous behavior.

There are several Add() overloads on IConfiguredInstance.Dependencies to add dependencies, or you can use the two helper methods for constructor parameters and setter properties shown in the following sections.

Add a Dependency for a Setter Property

If you already have a PropertyInfo for the concrete type (like you might in a policy or attribute usage) and you want to register an inline dependency, there is the Dependencies.AddForProperty() method as a convenience. For the actual value of the dependency, it needs to either be an object that can be cast to the property type or an Instance object that returns a type that can be cast to the property type.

With a value:


[Fact]
public void dependency_with_setter_with_value()
{
    var instance
        = new ConfiguredInstance(typeof(GuyWithProperties));
    var prop = instance.PluggedType.GetProperty("Widget");

    var myWidget = new ColorWidget("red");
    instance.Dependencies.AddForProperty(prop, myWidget);

    var container = new Container();

    container.GetInstance<GuyWithProperties>(instance)
        .Widget.ShouldBeTheSameAs(myWidget);
}

With an Instance for the dependency value:


[Fact]
public void dependency_with_setter_with_instance()
{
    var instance
        = new ConfiguredInstance(typeof(GuyWithProperties));
    var prop = instance.PluggedType.GetProperty("Widget");

    var dependency = new SmartInstance<AWidget>();
    instance.Dependencies.AddForProperty(prop, dependency);

    var container = new Container();

    container.GetInstance<GuyWithProperties>(instance)
        .Widget.ShouldBeOfType<AWidget>();
}

Add a Dependency for a Constructor Parameter

Likewise, you can add a dependency for a specific constructor parameter as either the actual value or an Instance object with the AddForConstructorParameter helper method:


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

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

[Fact]
public void specify_dependency_by_constructor_parameter()
{
    var instance = ConstructorInstance
        .For<GuyWithDatabaseConnection>();

    var parameter = instance.Constructor.GetParameters().Single();
    parameter.Name.ShouldBe("connectionString");

    var connString =
        "I haven't used sql server in years and I don't remember what connection strings look like";

    instance.Dependencies.AddForConstructorParameter(parameter, connString);

    var guy = new Container().GetInstance<GuyWithDatabaseConnection>(instance);

    guy.ConnectionString.ShouldBe(connString);
}

Adding Interceptors

You can add interceptors directly to a single IConfiguredInstance with code like this:



public class SimpleWidget
{
    public bool WasIntercepted = false;

    public void Intercept()
    {
        WasIntercepted = true;
    }
}

[Fact]
public void add_interceptor()
{
    var interceptor =
        new ActivatorInterceptor<SimpleWidget>(w => w.Intercept());
    var instance = new SmartInstance<SimpleWidget>();

    instance.AddInterceptor(interceptor);

    new Container().GetInstance<SimpleWidget>(instance)
        .WasIntercepted.ShouldBeTrue();
}

See Interception and Decorators for more information.