asp.net core 3.1 将 autofac 与 finbuckle 集成

sim*_*Vin 3 c# autofac asp.net-core

我正在构建一个带有多个数据库的多租户 asp.net core 3.1 应用程序(每个租户一个,一个主数据库)。我正在使用 Autofac 为我的服务提供单租户终身支持(现在我不需要租户覆盖,我只需要自定义 SinglePerTenant 生命周期)我正在使用

我的租户识别策略涉及对主数据库的 db 调用。

正如 autofac 的文档中所写,识别策略不应该调用数据库,因为它在每个依赖项解析中都会被调用。然后我使用了另一种租户识别解决方案(Finbuckle.MultiTenant)。

使用 Finbukle 在请求到达时调用他的识别策略(每个 htp 请求一次),我将 db 调用放在他的识别策略中(为了优化我可以缓存结果,并每天刷新一次查询)并设置一个租户信息对象在 HttpContext 中。

然后在 AutoFac 识别策略中,我尝试读取 FinBuckle 设置的对象,但这是不可能的,因为 Autofac 识别策略在 FinBuckle 之前调用,并且所需的属性为空。

我的 Program.cs 是:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}
Run Code Online (Sandbox Code Playgroud)

启动.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMultiTenant().WithStrategy<TestStategy>(ServiceLifetime.Singleton).WithStore<CustomTestStore>(ServiceLifetime.Singleton); //enable the multitenant support from finbukle

        services.AddControllers();

        services.AddAutofacMultitenantRequestServices();

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

        app.UseMultiTenant() //finbukle

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

    public void ConfigureContainer(ContainerBuilder builder)
    {
         builder.RegisterType<TestDiA>().As<ITestDI>().InstancePerTenant();
    }

    public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
    {
        // This is the MULTITENANT PART. Set up your tenant-specific stuff here.
        var strategy = new MyAutofacTenantIdentificationStrategy(container.Resolve<IHttpContextAccessor>());
        var mtc = new MultitenantContainer(strategy, container);
        return mtc;
    }
}
Run Code Online (Sandbox Code Playgroud)

Autofac租户识别策略:

public class MyAutofacTenantIdentificationStrategy : ITenantIdentificationStrategy
{
    private readonly IHttpContextAccessor httpContextAccessor;
    public MyAutofacTenantIdentificationStrategy(
      IHttpContextAccessor httpContextAccessor
    )
    {
        this.httpContextAccessor = httpContextAccessor;
    }
    public bool TryIdentifyTenant(out object tenantId)
    {
        tenantId = null;
        var context = httpContextAccessor.HttpContext;
        if (context == null)
            return false;


        var identifier = context.getTenatInfo()?.Identifier ?? null; //getTenantInfo is a method that extract the tenant info object setted by finbukle
        tenantId = identifier;
        return (tenantId != null || tenantId == (object)"");
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用 Autofac.AspNetCore.Multitenant 3.0.0、Autofac.Extensions.DependencyInjection 6.0.0 和 FinBuckle.MultiTenant 5.0.4

我在这方面真的很新,所以如果我问一个微不足道的问题,我深表歉意。有没有办法用这种方法解决问题?

或者我的问题有替代策略吗?

Tra*_*lig 5

目前我不相信 Finbuckle 和 Autofac.Multitenant 是兼容的。

Autofac 对 ASP.NET Core 的多租户支持依赖于在中间件管道中运行的第一件事,因此它可以设置HttpContext.RequestServices为基于租户范围。当然,作为其中的一部分,租户识别策略将运行。

但是,Finbuckle 假设每个租户都共享一个容器,就像默认的 ASP.NET Core 功能一样。Finbuckle 中间件尝试使用HttpContext.RequestServices基于注册策略来识别租户

您可以看到这引发的鸡/蛋问题 - 请求服务应该基于租户生命周期范围,但租户识别策略需要从请求服务中解决问题。

但是,让我们暂时忽略它,因为问题是如何避免每次解析时都调用数据库进行识别。

如果您深入研究 Finbuckle 代码,中间件会将租户信息设置HttpContext.Items为中间件运行的一部分。稍后,当您检索租户信息时,它会从 检索HttpContext.Items,而不是通过数据库重新解析。数据库调用仅执行一次,即第一次运行租户 ID。

那应该没问题。根据您计划支持的租户数量以及它们更改的频率,可能值得添加某种内存缓存层,您可以使用它来存储租户 ID 数据(无论存储在数据库中的任何内容都可以帮助您识别该租户)所以你可以在访问数据库之前先尝试内存存储。也许它会定期使那里的数据过期,或者它可能是固定大小或其他什么......缓存策略完全依赖于应用程序,我无法推荐相关细节。重点是,这是减轻数据库调用的一种方法。

但是回到鸡/蛋问题,我看不出有什么简单的方法可以解决这个问题。

如果是我,我必须得到这个工作,我可能会跳过调用IApplicationBuilder.UseMultiTenant()扩展,然后创建我自己的中间件版本,其中代替使用HttpContext.RequestServices,以获得租户ID策略,将采取多租户容器权在构造函数就像 Autofac 多租户中间件一样,并且会直接使用应用程序级容器来解决这些策略。当然,这必须在 Autofac 多租户请求服务中间件之前运行,并且强制中间件顺序有点痛苦。最后,由于在HttpContext.Items中间件运行后最终会获得租户标识,因此您的 AutofacITenantIdentificationStrategy 可以简单地查看那里获取数据,而根本不调用数据库。

然而...

非常重要的免责声明

  • 我一生中从未使用过 Finbuckle。我可以在 GitHub 上浏览代码,但我不知道上述考虑可能有什么副作用。
  • 我实际上还没有尝试过上述考虑。它可能不起作用。
  • 我是一名 Autofac 项目维护者,编写了最初的多租户支持,包括与 ASP.NET Core 的原始集成。这是非常痛苦的得到它的工作...所以当我说上面的考虑可能会非常棘手,我去过那儿。
  • 我非常明确地将其称为“考虑”——需要考虑的事情——而不是建议,因为我不一定会“推荐”我没有太大信心的东西。我没有信心,因为,再次,我不使用 Finbuckle。

我的实际建议是……嗯,放慢一点。正如您所提到的,您是这个领域的新手,而且您将在这里遇到的东西似乎很深。如果您还没有,我建议您深入研究 GitHub 上的实际 Finbuckle 代码。它看起来并不多,它可以让您了解正在发生的事情。我建议尝试创建一个包含 Autofac 多租户的多租户应用程序,而一个只包含芬扣。看看你是否真的需要两者。也许只有一个是有意义的。例如,Finbuckle 似乎已经有数据存储的多租户;这也是许多人使用 Autofac 多租户的目的 - 为每个租户注册不同的数据库上下文。也许只使用其中一种产品就足够了,这可以解决整个问题。