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.