【发布时间】:2018-03-24 00:14:51
【问题描述】:
请原谅我的非英语母语:
简而言之,租户覆盖默认 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 会先构建默认值,然后再构建租户。我当然可以为所有缺少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<IAgreementHandler> agreementHandlers是因为它可能会生成多个协议,每个协议做的都不一样。 -
DisbusementAgreementManager的内部逻辑(IAgreementHandler的外部)是否确定IAgreementHandler的运行方式或运行方式?还是所有逻辑都独立于IAgreementHandler并且它们都被允许自己运行?
标签: autofac multi-tenant