在 ConfigureMultitenantContainer 中解决依赖关系
Resolving dependencies in the ConfigureMultitenantContainer
我正在尝试解决 ConfigureMultitenantContainer
中的 ITenantIdentificationStrategy
但我有 An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
.
我已经在ConfigureContainer
中注册了TenantResolverStrategy
:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();
}
我想解析 ConfigureMultitenantContainer
中的 ITenantIdentificationStrategy
:
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
var strategy = container.Resolve<ITenantIdentificationStrategy>();
var mtc = new MultitenantContainer(strategy, container);
// mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantACustom>().As<ITenantCustom>());
return mtc;
}
但是它正在抛出 An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
。
我的ITenantIdentificationStrategy
是这样实现的:
public class TenantResolverStrategy : ITenantIdentificationStrategy
{
public TenantResolverStrategy(
IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
TenantEntity tenantEntity
)
{
this.httpContextAccessor = httpContextAccessor;
this.memoryCache = memoryCache;
this.tenantEntity = tenantEntity;
}
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var context = httpContextAccessor.HttpContext;
var hostName = context?.Request?.Host.Value;
tenantEntity = GetTenant(hostName);
if (tenantEntity != null)
{
tenantId = tenantEntity.TenantCode;
}
return (tenantId != null || tenantId == (object)"");
}
}
然后我在Program.cs
中注册ConfigureMultitenantContainer
如下:
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
我也无法解决我在 ConfigureContainer
中注册的其他依赖项。我的实现有什么问题吗?
有些事情可能会给您带来麻烦。
首先,我看到你的租户ID策略是不是单例。
builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();
这很麻烦,因为来自租户 ID 策略的每一个解决方案都将通过租户 ID 策略的单个实例。它将被缓存。但是,解析策略会解析不同的值,会产生误导。
考虑:
var strategy = container.Resolve<ITenantIdentificationStrategy>();
// The multitenant container is CACHING THIS.
var mtc = new MultitenantContainer(container, strategy);
// Now, later on you maybe resolve another instance of the strategy:
var anotherInstance = mtc.Resolve<ITenantIdentificationStrategy>();
// Or from the root:
var thirdInstance = container.Resolve<ITenantIdentificationStrategy>();
// OH NO! strategy != anotherInstance != thirdInstance
// These ARE NOT THE SAME INSTANCE. Tenant determination may CHANGE
// based on which one of these is used.
使您的租户 ID 策略成为单例。
接下来,由于策略缓存在多租户容器中,您无法维护状态。这非常重要,因为您将 运行 陷入大量线程问题。
public class TenantResolverStrategy : ITenantIdentificationStrategy
{
public TenantResolverStrategy(
IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
TenantEntity tenantEntity
)
{
this.httpContextAccessor = httpContextAccessor;
this.memoryCache = memoryCache;
// PROBLEM! Where is TenantEntity coming from?
this.tenantEntity = tenantEntity;
}
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var context = httpContextAccessor.HttpContext;
var hostName = context?.Request?.Host.Value;
// PROBLEM: Incorrectly storing state in the strategy
// when this is used across threads. (There's also no
// explanation of what's in GetTenant, so it's hard to
// help with that.)
tenantEntity = GetTenant(hostName);
if (tenantEntity != null)
{
tenantId = tenantEntity.TenantCode;
}
return (tenantId != null || tenantId == (object)"");
}
}
您可以维护缓存,但不要维护状态。例如,您可能需要一个Dictionary<string, object>
来缓存主机名到租户 ID 的映射,这就是很好(只要你锁定它,或者使用线程安全的字典)。但是您有一个 单个对象 可以跨线程覆盖,这是个坏消息。
接下来,我看到您的租户 ID 策略需要依赖项。 通常,我会避免这种情况并直接构建它。我知道这对某些人来说不是很好,但是有一种倾向 "over-DI" 不应该涉及的事情迪。手动构建 ContainerBuilder
之类的基础对象或您的租户 ID 策略可确保您只查看可以控制的内容(并且它避免了您所见的这些异常)。
您应该能够手动解决任何进入租户 ID 策略的依赖关系。例如,这应该有效:
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
// These are the dependencies of the strategy. You don't NEED TO DO THIS
// but if you put these in here, it SHOULD NOT BLOW UP. If it does, you know
// where to start tracing things down.
var accessor = container.Resolve<IHttpContextAccessor>();
var cache = container.Resolve<IMemoryCache>();
var entity = container.Resolve<TenantEntity>();
// Here's the strategy - again, make sure it's a SINGLETON!
var strategy = container.Resolve<ITenantIdentificationStrategy>();
var mtc = new MultitenantContainer(strategy, container);
// mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantACustom>().As<ITenantCustom>());
return mtc;
}
也就是说,我知道可能需要注入诸如数据库连接之类的东西,在这种情况下,再次确保将东西标记为 单例。您的多租户容器租户 ID 策略将在应用程序的整个生命周期内有效。另外,任何依赖于租户 ID 策略的东西(如多租户容器)都不应该是特定于租户或基于请求的,因为……如果没有有效的租户 ID 策略,您将无法确定租户。循环依赖!
所以,全部煮沸:
- 将您的租户 ID 策略注册为单例。
- 删除租户 ID 策略中所有非单例的依赖项(例如,
TenantEntity
)。
- 对主机到租户 ID 的映射使用线程安全缓存(例如内存缓存),但不存储状态(不要保留
TenantEntity
实例变量;将其作为方法-level 局部变量,如果需要的话)。
- 确保您需要解决的所有问题都已注册。如果您的租户 ID 策略需要
IHttpContextAccessor
(或 IMemoryCache
或其他),则需要注册。如果您 运行 遇到麻烦,请尝试直接解决这些依赖关系;这将准确地告诉您哪个组件有问题。 (但是,如果您查看所获得的异常的完整堆栈跟踪,您应该确切地看到发生了什么。您没有在问题中包含该异常消息,因此我们无法深入研究。)
我正在尝试解决 ConfigureMultitenantContainer
中的 ITenantIdentificationStrategy
但我有 An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
.
我已经在ConfigureContainer
中注册了TenantResolverStrategy
:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();
}
我想解析 ConfigureMultitenantContainer
中的 ITenantIdentificationStrategy
:
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
var strategy = container.Resolve<ITenantIdentificationStrategy>();
var mtc = new MultitenantContainer(strategy, container);
// mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantACustom>().As<ITenantCustom>());
return mtc;
}
但是它正在抛出 An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
。
我的ITenantIdentificationStrategy
是这样实现的:
public class TenantResolverStrategy : ITenantIdentificationStrategy
{
public TenantResolverStrategy(
IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
TenantEntity tenantEntity
)
{
this.httpContextAccessor = httpContextAccessor;
this.memoryCache = memoryCache;
this.tenantEntity = tenantEntity;
}
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var context = httpContextAccessor.HttpContext;
var hostName = context?.Request?.Host.Value;
tenantEntity = GetTenant(hostName);
if (tenantEntity != null)
{
tenantId = tenantEntity.TenantCode;
}
return (tenantId != null || tenantId == (object)"");
}
}
然后我在Program.cs
中注册ConfigureMultitenantContainer
如下:
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
我也无法解决我在 ConfigureContainer
中注册的其他依赖项。我的实现有什么问题吗?
有些事情可能会给您带来麻烦。
首先,我看到你的租户ID策略是不是单例。
builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();
这很麻烦,因为来自租户 ID 策略的每一个解决方案都将通过租户 ID 策略的单个实例。它将被缓存。但是,解析策略会解析不同的值,会产生误导。
考虑:
var strategy = container.Resolve<ITenantIdentificationStrategy>();
// The multitenant container is CACHING THIS.
var mtc = new MultitenantContainer(container, strategy);
// Now, later on you maybe resolve another instance of the strategy:
var anotherInstance = mtc.Resolve<ITenantIdentificationStrategy>();
// Or from the root:
var thirdInstance = container.Resolve<ITenantIdentificationStrategy>();
// OH NO! strategy != anotherInstance != thirdInstance
// These ARE NOT THE SAME INSTANCE. Tenant determination may CHANGE
// based on which one of these is used.
使您的租户 ID 策略成为单例。
接下来,由于策略缓存在多租户容器中,您无法维护状态。这非常重要,因为您将 运行 陷入大量线程问题。
public class TenantResolverStrategy : ITenantIdentificationStrategy
{
public TenantResolverStrategy(
IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
TenantEntity tenantEntity
)
{
this.httpContextAccessor = httpContextAccessor;
this.memoryCache = memoryCache;
// PROBLEM! Where is TenantEntity coming from?
this.tenantEntity = tenantEntity;
}
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var context = httpContextAccessor.HttpContext;
var hostName = context?.Request?.Host.Value;
// PROBLEM: Incorrectly storing state in the strategy
// when this is used across threads. (There's also no
// explanation of what's in GetTenant, so it's hard to
// help with that.)
tenantEntity = GetTenant(hostName);
if (tenantEntity != null)
{
tenantId = tenantEntity.TenantCode;
}
return (tenantId != null || tenantId == (object)"");
}
}
您可以维护缓存,但不要维护状态。例如,您可能需要一个Dictionary<string, object>
来缓存主机名到租户 ID 的映射,这就是很好(只要你锁定它,或者使用线程安全的字典)。但是您有一个 单个对象 可以跨线程覆盖,这是个坏消息。
接下来,我看到您的租户 ID 策略需要依赖项。 通常,我会避免这种情况并直接构建它。我知道这对某些人来说不是很好,但是有一种倾向 "over-DI" 不应该涉及的事情迪。手动构建 ContainerBuilder
之类的基础对象或您的租户 ID 策略可确保您只查看可以控制的内容(并且它避免了您所见的这些异常)。
您应该能够手动解决任何进入租户 ID 策略的依赖关系。例如,这应该有效:
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
// These are the dependencies of the strategy. You don't NEED TO DO THIS
// but if you put these in here, it SHOULD NOT BLOW UP. If it does, you know
// where to start tracing things down.
var accessor = container.Resolve<IHttpContextAccessor>();
var cache = container.Resolve<IMemoryCache>();
var entity = container.Resolve<TenantEntity>();
// Here's the strategy - again, make sure it's a SINGLETON!
var strategy = container.Resolve<ITenantIdentificationStrategy>();
var mtc = new MultitenantContainer(strategy, container);
// mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantACustom>().As<ITenantCustom>());
return mtc;
}
也就是说,我知道可能需要注入诸如数据库连接之类的东西,在这种情况下,再次确保将东西标记为 单例。您的多租户容器租户 ID 策略将在应用程序的整个生命周期内有效。另外,任何依赖于租户 ID 策略的东西(如多租户容器)都不应该是特定于租户或基于请求的,因为……如果没有有效的租户 ID 策略,您将无法确定租户。循环依赖!
所以,全部煮沸:
- 将您的租户 ID 策略注册为单例。
- 删除租户 ID 策略中所有非单例的依赖项(例如,
TenantEntity
)。 - 对主机到租户 ID 的映射使用线程安全缓存(例如内存缓存),但不存储状态(不要保留
TenantEntity
实例变量;将其作为方法-level 局部变量,如果需要的话)。 - 确保您需要解决的所有问题都已注册。如果您的租户 ID 策略需要
IHttpContextAccessor
(或IMemoryCache
或其他),则需要注册。如果您 运行 遇到麻烦,请尝试直接解决这些依赖关系;这将准确地告诉您哪个组件有问题。 (但是,如果您查看所获得的异常的完整堆栈跟踪,您应该确切地看到发生了什么。您没有在问题中包含该异常消息,因此我们无法深入研究。)