asp.netinversion-of-controlstructuremapstructuremap4

StructureMap 4 with named instances not working as expected


Here is a simplified version of a couple of classes in my solution and the interfaces they implement. They both share one interface and also implement a dedicated interface.

public interface ISharedContract
{
    void ImplementSharedContract();
}

public interface IConcreteOne
{
    void ImplementConcreteOne();
}

public interface IConcreteTwo
{
    void ImplementConcreteTwo();
}

public class ConcreteOne : BaseConcrete, IConcreteOne, ISharedContract
{
    public void ImplementSharedContract()
    {
        this.ImplementConcreteOne();
    }

    public void ImplementConcreteOne()
    {
    }
}

public class ConcreteTwo : BaseConcrete, IConcreteTwo, ISharedContract
{
    public void ImplementSharedContract()
    {
        this.ImplementConcreteTwo();
    }

    public void ImplementConcreteTwo()
    {
    }
}

My StructureMap registry registers these dependencies as follows:

public class MyRegistry :  Registry
{
    public MyRegistry()
    {
        this.For<ISharedContract>().Use<ConcreteOne>().Named("cOne");
        this.For<ISharedContract>().Use<ConcreteTwo>().Named("cTwo");
        this.For<IConcreteOne>().Use<ConcreteOne>();
        this.For<IConcreteTwo>().Use<ConcreteTwo>();
    }
}

Finally I have a class which injects these dependencies in the constructor as follows:

public MyDependent(ISomethingElse somethingElse, ISharedContract cOne, ISharedContract cTwo)
{
    this.collection = new List<ISharedContract>()
    {
        cOne,
        cTwo
    };
}

At runtime I observe that cOne and cTwo are both injected instances of ConcreteTwo. If I swap the order of the two named registrations in the StructureMap registry then cOne and cTwo are both instances of ConcreteOne.

This isn't the first time I've used named instances but I've not observed this before. Is there something I'm missing? I've checked and double-checked that the values passed to the Named method correspond with the parameter names in the constructor.

Update

Here is the updated registry entry which is needed to make my example case work. Thanks to @jeremy-d-miller for the info I needed:

this.For<IMyDependent>().Use<MyDependent>()
    .Ctor<ISharedContract>("cOne").Is<ConcreteOne>()
    .Ctor<ISharedContract>("cTwo").Is<ConcreteTwo>();

Solution

  • StructureMap doesn't have any magic functionality here that is matching your constructor parameter names to the named instance of that type. You would have to explicitly map the inline dependencies in this case: http://structuremap.github.io/registration/inline-dependencies/

    In this code:

    this.For<ISharedContract>().Use<ConcreteOne>().Named("cOne"); this.For<ISharedContract>().Use<ConcreteTwo>().Named("cTwo");

    You're setting the default ISharedContext to ConcreteOne, then immediately overriding that to ConcreteTwo. Last one wins in this case. When you're building your MyDependent class, StructureMap is just using auto-wiring to push in the default instance of ISharedContext.