代码之家  ›  专栏  ›  技术社区  ›  Xin Zhang

Autofac多租户覆盖和IEnumerable注入/解析

  •  1
  • Xin Zhang  · 技术社区  · 6 年前

    请原谅我的非母语英语:

    简而言之,租户覆盖默认值的最佳方式是什么 IEnumerable<T> 登记

    TL;医生,我有个服务 ServiceToBeResove(IEnumerable<IShitty> svcs) 需要一个 IEnumerable<IShitty> 依赖关系,但我们发现并非所有租户都将服务注册为 IShitty ,因此在我们的应用程序容器中,我们创建了一个未实现的 NoImplementShitty 并将其注册为 IShitty公司 将服务器作为默认服务器,以使解决过程顺利进行,如果租户已注册,则会获得特定于租户的信息,如果租户忘记注册,则不会实现此默认信息。但我们很快就会发现 ServiceToBeResove 将对两个租户进行注册 IShitty公司 和默认值 无实施能力 因为它依赖于IEnumerable。我真正想要的 IEnumerable<IShitty> 依赖关系仅用于租户注册(注册1个或多个),如果租户未注册,则只需使用默认值 无实施能力 作为 IEnumerable<IShitty> . 我玩过 .OnlyIf() , OnlyIfRegistered() , .PreventDefault() 在应用程序容器上,这确实没有帮助,因为autofac将首先构建默认值,然后构建租户。我当然可以使用 无实施能力 对于所有未登记的承租人 IShitty公司 但它似乎没有利用多租户的覆盖默认功能。

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

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

    在我们的租户中

    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 ,似乎有 NoOpAgreementHandler 我想我们只会 租户协议处理人 . 但如果我们将PaymentAgreementManager更改为

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

    我们只会得到 租户协议处理人 这是意料之中的。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Marc L. Rahil Ahmad    6 年前

    Autofac的默认行为是有原因的。要求it部门采取不同的做法将在依赖项注入级别添加应用程序逻辑,这违反了关注点的分离(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 需要重构。


    还有另一个问题:

    我们添加no op的原因是我们有注册/解析的单元测试,以确保所有注册都已正确配置。

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