请原谅我的非母语英语:
简而言之,租户覆盖默认IEnumerable<T>
注册的最佳方式是什么?
TL; DR所以我有一个服务ServiceToBeResove(IEnumerable<IShitty> svcs)
需要一个IEnumerable<IShitty>
依赖项,但我们发现并非所有租户都有注册为IShitty
的服务,所以在我们的应用程序容器中我们创建了一个没有实现NoImplementShitty
并将其作为IShitty
的TypeService注册到服务器作为默认设置以使解决过程满意,如果租户已注册,我们确实获得特定于租户的权限,如果租户忘记,则默认未实现注册。但我们很快发现ServiceToBeResove
将为两个租户实现注册IShitty
,并且默认NoImplementShitty
因为它依赖于IEnumerable。我真正想要的IEnumerable<IShitty>
依赖项仅用于租户注册(注册1个或更多),如果租户未注册,只需使用默认NoImplementShitty
作为IEnumerable<IShitty>
。我在应用容器上使用了.OnlyIf()
,OnlyIfRegistered()
,.PreventDefault()
,但实际上没有帮助,因为autofac会首先构建默认值,然后是租户。我当然可以将NoImplementShitty
用于错过IShitty
注册的所有租户,但它似乎没有利用多租户的覆盖默认功能。
更具体地说,在我们的基地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
。
答案 0 :(得分:1)
Autofac的默认行为是有原因的。要求它以不同的方式执行它将在依赖注入级别添加应用程序逻辑,这违反了关注点的分离(DI应该只注入依赖关系)并直接导致令人惊讶的行为(“为什么DI不注入每个可用的组件?”)并削弱了系统的可维护性。
这可能不是问题。
逻辑在每个
IAgreementHandler
内都是自包含的。
如果是这样,在DisbursementAgreementManager
调用它们时,它们都被调用,然后执行它们自己的逻辑(可能包括决定是否全部做任何事情)。 E.g:
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
甚至不应该在您的主要项目中,只是单元测试项目的成员。