【问题标题】:Autofac Multi Tenant override and IEnumerable<T> inject/resolveAutofac 多租户覆盖和 IEnumerable<T> 注入/解析
【发布时间】:2018-03-24 00:14:51
【问题描述】:

请原谅我的非英语母语:

简而言之,租户覆盖默认 IEnumerable&lt;T&gt; 注册的最佳方式是什么?

TL;DR 所以我有一个服务ServiceToBeResove(IEnumerable&lt;IShitty&gt; svcs) 需要一个IEnumerable&lt;IShitty&gt; 依赖项,但我们发现并非所有租户都将服务注册为IShitty,所以在我们的应用程序容器中,我们创建了一个未实现的NoImplementShitty 和将其注册为 IShitty 的 TypeService 到服务器作为默认服务,以使解析过程愉快,如果租户已注册,我们会获得特定于租户的信息,如果租户忘记注册,则此默认值未实现。但我们很快发现ServiceToBeResove 将让两个租户实现注册IShitty 和默认NoImplementShitty,因为它依赖于IEnumerable。我真正想要的 IEnumerable&lt;IShitty&gt; 依赖项只是使用已注册的租户(注册 1 个或更多),如果租户未注册,只需使用默认的 NoImplementShitty 作为 IEnumerable&lt;IShitty&gt;。我在应用程序容器上玩过.OnlyIf()、OnlyIfRegistered()、.PreventDefault(),这真的没有帮助,因为 autofac 会先构建默认值,然后再构建租户。我当然可以为所有缺少IShitty 注册的租户使用NoImplementShitty,但它似乎没有利用多个租户的覆盖默认功能。

更具体地说,在我们的基地AgreementModule 中,我们有

builder.RegisterType<NoOpAgreementHandler>() //NoOpAgreementHandler is the IShitty
    .As<IAgreementHandler>()
    .InstancePerLifetimeScope();

在我们的租户A中,我们有

public class TenantAContainerBuilder : ITenantContainerBuilder
{
    public virtual object TenantId => "1";
    public virtual void Build(ContainerBuilder builder)
    {
        builder.RegisterType<TenantAAgreementHandler>()
            .As<IAgreementHandler>()
            .InstancePerLifetimeScope();
    }
}

我们构建容器如下:

var appContainer = builder.Build();
var tenantIdentifier = new ManualTenantIdentificationStrategy(); //We have our own strategy here I just use the ManualTenantIdentificationStrategy for example
var multiTenantContainer = new MultitenantContainer(tenantIdentifier, appContainer);
//GetTenantContainerBuilders will basically give you all TenantBuilder like TenantAContainerBuilder above
foreach (IGrouping<object, ITenantContainerBuilder> source in GetTenantContainerBuilders().GroupBy(x => x.TenantId))
{
    var configurationActionBuilder = new ConfigurationActionBuilder();
    configurationActionBuilder.AddRange(source.Select(x => new Action<ContainerBuilder>(x.Build)));
    multiTenantContainer.ConfigureTenant(source.Key, configurationActionBuilder.Build());
}

当尝试解析服务时,如果我们这样做:

public DisbursementAgreementManager(IEnumerable<IAgreementHandler> agreementHandlers)
{
    _agreementHandlers = agreementHandlers;
}

agreementHandlers 将是NoOpAgreementHandler 和TenantAAgreementHandler 的IEnumerable,拥有NoOpAgreementHandler 似乎很奇怪,我认为我们只会得到TenantAAgreementHandler。但是如果我们将 DisbursementAgreementManager 更改为

public DisbursementAgreementManager(IAgreementHandler agreementHandler)
{
    _agreementHandler = agreementHandler;
}

我们只会得到预期的TenantAAgreementHandler。

【问题讨论】:

  • 如何实现多租户?你如何注册你的IShitty 实现?
  • @CyrilDurand 抱歉回复晚了,我在上面添加了详细的实现,如果您感到困惑,请告诉我您还需要什么?
  • 这里涉及到的实现的动态选择似乎涉及到了超出依赖注入目的的逻辑。一般来说,如果您正在解析一个实现集合,那么获得一个空集合是完全合理的:要么全部运行,要么如果为空则什么都不运行。在这种情况下,无操作是多余的。这个问题给人一种X-Y problem 的感觉,所以让我问一下:为什么 DisbursementAgreementManager 需要 注册才能以这种方式从容器中解析?
  • @MarcL。我们添加无操作的原因是我们对注册/解析进行了单元测试,以确保正确配置所有注册。 DisbursementAgreementManager 之所以需要IEnumerable&lt;IAgreementHandler&gt; agreementHandlers 是因为它可能会生成多个协议,每个协议做的都不一样。
  • DisbusementAgreementManager 的内部逻辑(IAgreementHandler 的外部)是否确定 IAgreementHandler 的运行方式或运行方式?还是所有逻辑都独立于IAgreementHandler 并且它们都被允许自己运行?

标签: autofac multi-tenant


【解决方案1】:

Autofac 的默认行为是有原因的。要求它以不同的方式执行它将在依赖注入级别添加应用程序逻辑,这违反了关注点分离(DI 应该只注入依赖项)并直接导致令人惊讶的行为(“为什么 DI 没有注入每个可用组件?”)并削弱了系统的可维护性。

这可能不是问题。

每个IAgreementHandler内部的逻辑都是独立的。

如果是这样,在它们被DisbursementAgreementManager 调用时,它们都会被调用,然后执行它们自己的逻辑(可能包括决定是否全部执行任何操作)。例如:

foreach (var ah in _agreementHandlers) ah.Agree(disbursementInfo);

或者类似的东西

foreach (var ah in _agreementHandlers.Where(a => a.ShouldRun(data) || overridingCondition)) 
{
    var agreement = ah.Agree(info);
    this.Process(agreement);
}

或其他。关键是如果NoOpAgreementHandler 正在做它应该做的事情(也就是说,什么都不做),那么它在被调用时应该没有任何效果。没问题。

如果情况与描述不同,则需要重构 NoOpAgreementHandler 和可能的 IAgreementHandler。


还有一点值得关注:

我们添加无操作的原因是我们对注册/解析进行了单元测试,以确保正确配置所有注册。

您的测试要求正在渗透到您的主要逻辑中。这些 DI 配置测试应该独立于生产 DI 配置。 NoOpAgreementHandler 甚至不应该在你的主要项目中,而只是单元测试项目的成员。

【讨论】:

  • 谢谢。但是如果我在单元测试中隔离NoOpAgreementHandler,我怎么知道我是否在运行前错过了IAgreementHandler 的一些注册?或者更恰当地说,单元测试在部署之前确保所有服务都为单元测试正确注册的良好模式是什么?
  • 我不确定我是否理解这个问题。 NoOpAgreementHandler 如何帮助您测试其他组件的注册?
猜你喜欢
  • 2021-03-20
  • 1970-01-01
  • 2012-06-19
  • 2022-10-25
  • 2011-06-07
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多