简体   繁体   English

在 ConfigureMultitenantContainer 中解决依赖关系

[英]Resolving dependencies in the ConfigureMultitenantContainer

I am trying to resolve ITenantIdentificationStrategy in the ConfigureMultitenantContainer but I am having An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll .我正在尝试解决ConfigureMultitenantContainer中的ITenantIdentificationStrategy但我An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll

I have registered the TenantResolverStrategy in the ConfigureContainer :我已经在ConfigureContainer中注册了TenantResolverStrategy

public void ConfigureContainer(ContainerBuilder builder)
{
  builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();
}

I want to resolve the ITenantIdentificationStrategy in the ConfigureMultitenantContainer :我想解决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;
}

However it is throwing An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll .但是它抛出An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll

My ITenantIdentificationStrategy is implemented this way:我的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)"");
  }
}

And I register the ConfigureMultitenantContainer in the Program.cs as follows:我在Program.cs中注册了ConfigureMultitenantContainer ,如下所示:

var host = Host.CreateDefaultBuilder(args)
    .UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))

I couldn't resolve other dependencies which I register in the ConfigureContainer as well.我也无法解决我在ConfigureContainer中注册的其他依赖项。 Is there anything wrong with my implementation?我的实施有什么问题吗?

There are a few things that may be causing you trouble here.这里有几件事可能会给您带来麻烦。

First, I see your tenant ID strategy is not a singleton .首先,我看到您的租户 ID 策略不是 singleton

builder.RegisterType<TenantResolverStrategy>().As<ITenantIdentificationStrategy>();

That's trouble, because every single resolution from the tenant ID strategy is going to go through a single instance of the tenant ID strategy.这很麻烦,因为租户 ID 策略的每个解析都通过租户 ID 策略的单个实例转到 go。 It's going to be cached.它将被缓存。 However, resolving the strategy is going to resolve different values and be misleading.但是,解决策略将解决不同的价值观并具有误导性。

Consider:考虑:

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.

Make your tenant ID strategy a singleton.将您的租户 ID 策略设为 singleton。

Next, since the strategy is cached in the multitenant container, you can't maintain state .接下来,由于策略缓存在多租户容器中,因此您无法维护 state This is super important because you will run into tons of threading issues.这非常重要,因为您会遇到大量线程问题。

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)"");
  }
}

You can maintain a cache, but don't maintain state.您可以维护缓存,但不要维护 state。 For example, maybe you need a Dictionary<string, object> to cache hostname to tenant ID mappings, and that's fine (as long as you do locking around that, or use a thread-safe dictionary).例如,也许您需要一个Dictionary<string, object>来缓存主机名到租户 ID 的映射,这很好(只要您锁定它,或者使用线程安全字典)。 But you have a single object that can be overwritten across threads and that's bad news.但是你有一个可以跨线程覆盖的object ,这是个坏消息。

Next, I see your tenant ID strategy takes dependencies.接下来,我看到您的租户 ID 策略需要依赖项。 Generally, I'd avoid this and construct it directly.一般来说,我会避免这种情况并直接构建它。 I know that's not great for some folks, but there is a tendency to "over-DI" things that shouldn't be involved in DI.我知道这对某些人来说不是很好,但是有一种趋势是“过度 DI”不应该涉及 DI 的事情。 Manually constructing base objects like a ContainerBuilder or your tenant ID strategy ensures you're only looking at things that you can control (and it avoids these exceptions like you've seen).手动构建像ContainerBuilder或租户 ID 策略这样的基础对象可确保您只查看您可以控制的内容(并且它可以避免这些异常,就像您所看到的那样)。

You should be able to manually resolve any dependencies that go into the tenant ID strategy.您应该能够手动将 go 的任何依赖项解析为租户 ID 策略。 For example, this should work:例如,这应该有效:

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;
}

That said, I recognize that things like database connections may need to be injected, in which case, again, be sure things are marked as singletons .也就是说,我认识到可能需要注入诸如数据库连接之类的东西,在这种情况下,再次确保将事物标记为singletons Your multitenant container tenant ID strategy will live for the lifetime of the application.您的多租户容器租户 ID 策略将在应用程序的整个生命周期内有效。 Plus, nothing that depends on a tenant ID strategy (like the multitenant container) should be tenant-specific or request-based because... you can't determine the tenant without a valid tenant ID strategy.此外,任何依赖于租户 ID 策略(如多租户容器)的内容都不应是特定于租户或基于请求的,因为......如果没有有效的租户 ID 策略,您将无法确定租户。 Circular dependency!循环依赖!

So, boiling it all down:所以,把它全部煮沸:

  • Register your tenant ID strategy as a singleton.将您的租户 ID 策略注册为 singleton。
  • Remove all dependencies in the tenant ID strategy that aren't singletons (eg, TenantEntity ).删除租户 ID 策略中不是单例的所有依赖项(例如, TenantEntity )。
  • Use thread-safe caching (eg, the memory cache) for host-to-tenant-ID mappings, but do not store state (don't keep that TenantEntity instance variable; make it a method-level local variable if you need to).使用线程安全缓存(例如,memory 缓存)进行主机到租户 ID 映射,但不要存储 state(不要保留该TenantEntity实例变量;如果需要,将其设为方法级别的局部变量) .
  • Make sure everything you need to resolve is registered.确保您需要解决的所有问题都已注册。 If your tenant ID strategy needs IHttpContextAccessor (or IMemoryCache , or whatever) that needs to be registered.如果您的租户 ID 策略需要需要注册的IHttpContextAccessor (或IMemoryCache或其他)。 Try resolving those dependencies directly if you run into trouble;如果遇到麻烦,请尝试直接解决这些依赖关系; that will tell you exactly which component is having problems.这将准确地告诉您哪个组件有问题。 (However, if you look through the full stack trace of the exception you're getting, you should see exactly what's going on. You didn't include that exception message in the question, so we can't dive in.) (但是,如果您查看所获得异常的完整堆栈跟踪,您应该确切地看到发生了什么。您没有在问题中包含该异常消息,因此我们无法深入研究。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM