Home > Software engineering >  StructureMap for same interface but with multiple instances for different constructor parameters
StructureMap for same interface but with multiple instances for different constructor parameters

Time:10-30

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.

CodePudding user response:

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());

CodePudding user response:

I had already a working solution before i saw Evk's answer(which i will check now):

IGenericFormInitialValueResolver genericFormResolver = _iocContainer.GetAllInstances(actualType)
    .OfType<IGenericFormInitialValueResolver>()
    .FirstOrDefault(resolver => resolver is ISalesforceInstanceFormInitialValueResolver sr && sr.SalesforceInstanceName == salesforceInstanceName);
if (genericFormResolver != null)
{
    return genericFormResolver;
}

So i'm using iocContainer.GetAllInstances to get all(3) instances and then filter the one i need. I guess Evk's approach is cleaner and might me more efficient.

Update: i tested Evk's approach and it's working fine. I get always a cached instanced for each SalesforceInstanceName and what makes it better than mine is: only the instance needed is constructed and not all. I keep this answer in case someone needs an alternative.

  • Related