【问题标题】:StructureMap for same interface but with multiple instances for different constructor parametersStructureMap 用于相同的接口,但具有用于不同构造函数参数的多个实例
【发布时间】:2021-10-29 21:21:45
【问题描述】:

我正在为 StructureMap 苦苦挣扎。我需要确保每个HttpContextLifecycle 都有一个实例,但每个构造函数参数都有一个实例。我们的旧工作代码是:

注册表:

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

FormInitialValueResolverFactory(actualType 是IGenericFormInitialValueResolver 泛型类型):

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

现在我需要更改它,以便在构造函数中给出SalesforceInstanceName。但我仍然希望对于每个SalesforceInstanceName(它是一个具有 2 个值的枚举)实例都为HttpContextLifecycle 缓存。所以我通过TryGetInstance重载传递参数:

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

这以一种我得到实例但总是一个新实例的方式起作用,因此它不会在每个 http 生命周期中缓存。

所以我尝试更改注册表:

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

但现在TryGetInstance 总是返回null

【问题讨论】:

    标签: c# dependency-injection structuremap


    【解决方案1】:

    如果您通过ExplicitArguments 传递自定义构造函数参数,StructureMap 设计将始终构建一个新实例。您可能希望像这样使用命名实例:

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

    您基本上说,对于 SalesforceInstanceName 枚举的每个值,您都注册了 SalesforceEditFormInitialValueResolver 的单独命名实例(具有所述枚举值的名称),并将该枚举值作为构造函数参数。

    然后解决,而不是使用ExplicitArguments,使用名称:

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

    【讨论】:

    • 感谢您的回答。我现在将对其进行测试。同时我发现了一种不同的工作方法(见my answer)。我猜你的效率更高,更清洁,不是吗?
    • 我想是的。例如,由于您想要缓存这些实例 - 它们的构建成本有些高。在您的方法中,即使您需要 1,您也始终构建所有 3(通过解决所有 3)。在我的方法中,仅构建一个。
    • 你说得对,再次感谢
    【解决方案2】:

    在我看到Evk's answer(我现在要检查)之前,我已经有了一个可行的解决方案:

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

    所以我使用iocContainer.GetAllInstances 来获取所有(3)个实例,然后过滤我需要的那个。我猜 Evk 的方法更干净,可能我更有效率。

    更新:我测试了 Evk 的方法,它工作正常。我总是为每个SalesforceInstanceName 获得一个缓存实例,它比我的更好的是:只构造所需的实例,而不是全部。我保留这个答案以防有人需要替代方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      相关资源
      最近更新 更多