Search code examples
c#dependency-injectionstructuremap

StructureMap for same interface but with multiple instances for different constructor parameters


I'm struggling with StructureMap. I need to ensure that i get an instance per HttpContextLifecycle but one for each constructor parameter. Our old working code was:

Registry:

For<IGenericFormInitialValueResolver<SalesforceEditContactBlock>>()
    .LifecycleIs<HttpContextLifecycle>()
    .Use<SalesforceEditFormInitialValueResolver>();

In FormInitialValueResolverFactory(actualType is the IGenericFormInitialValueResolver generic type):

if (_iocContainer.TryGetInstance(actualType) is IGenericFormInitialValueResolver returnValue)
{
    if (returnValue is ISalesforceInstanceFormInitialValueResolver salesforceInstanceFormInitialValueResolver &&
        salesforceInstanceName.HasValue)
    {
        salesforceInstanceFormInitialValueResolver.SalesforceInstanceName = salesforceInstanceName.Value;
    }
    return returnValue;
}

Now i need to change it, so that the SalesforceInstanceName is given in the constructor. But i still want that for each SalesforceInstanceName(it's an enum with 2 values) the instance is cached for the HttpContextLifecycle. So i passed the parameter via TryGetInstance overload:

ExplicitArguments args = new ExplicitArguments();
args.Set(salesforceInstanceName ?? SalesforceInstanceName.Undefined);
if (_iocContainer.TryGetInstance(actualType, args) is IGenericFormInitialValueResolver returnValue)
{
    return returnValue;
}

That worked in a way that i got the instance but always a new one, so it's not cached per http lifecycle.

So i have tried to change the registry:

foreach (SalesforceInstanceName salesforceInstanceName in (SalesforceInstanceName[])Enum.GetValues(typeof(SalesforceInstanceName)))
{
    For<IGenericFormInitialValueResolver<SalesforceEditContactBlock>>()
        .Add<SalesforceEditFormInitialValueResolver>().Ctor<SalesforceInstanceName>()
        .Is(salesforceInstanceName)
        .LifecycleIs<HttpContextLifecycle>();
}

But now TryGetInstance always returns null.


Solution

  • StructureMap by design will always build a fresh instance if you pass custom constructor parameters via ExplicitArguments. You might want to use named instances instead like this:

    foreach (SalesforceInstanceName salesforceInstanceName in (SalesforceInstanceName[])Enum.GetValues(typeof(SalesforceInstanceName)))
    {
        For<IGenericFormInitialValueResolver<SalesforceEditContactBlock>>()
            .Use<SalesforceEditFormInitialValueResolver>()
            .Ctor<SalesforceInstanceName>()
            .Is(salesforceInstanceName)
            .Named(salesforceInstanceName.ToString())
            .LifecycleIs<HttpContextLifecycle>();
    }
    

    You basically say that for each value of SalesforceInstanceName enum you register separate named instance (with name of said enum value) of SalesforceEditFormInitialValueResolver with that enum value as constructor argument.

    Then to resolve, instead of using ExplicitArguments, use name:

    container.TryGetInstance(actualType, salesforceInstanceName.ToString());