当前位置 : 主页 > 网络编程 > ASP >

asp.net-core – aspnet核心应用程序中的Autofac.Multitenant似乎无法正确解析租户作用域

来源:互联网 收集:自由互联 发布时间:2021-06-24
我正在升级使用Autofac.Multitenant框架的Multitenant dotnet核心解决方案.我没有很多运气让租约解决方案正常工作.我在这里创建了一个简单的问题演示: https://github.com/SaltyDH/AutofacMultitenancy1
我正在升级使用Autofac.Multitenant框架的Multitenant dotnet核心解决方案.我没有很多运气让租约解决方案正常工作.我在这里创建了一个简单的问题演示: https://github.com/SaltyDH/AutofacMultitenancy1

此repo演示了注册InstancePerTenant作用域依赖关系TestMultitenancyContext,该关系在Home Controller中解析.由于使用IHttpContextAccessor的问题,我使用自定义RequestMiddleware类来捕获当前的HttpContext对象,以便我可以在MultitenantIdentificationStrategy中的当前HttpContext请求对象上执行逻辑.

最后,TestFixture提供了一个简单的xUnit测试,至少在我的机器上为两个租户返回“tenant1”.

有没有我在这里错过的或者这只是目前没有工作?

更新10/6/2017: We released Autofac.AspNetCore.Multitenant以更易于使用的软件包结束解决方案.我会在这里留下原始答案/解释给后人,但是如果你打这个,你可以抓住那个包然后继续前进.

我认为你遇到了时间问题.

如果在中间件中弹出HttpContext上的调试器,则可以看到在名为ServiceProvidersFeature的属性上有一个RequestServicesFeature对象. That’s what’s responsible for creating the per-request scope.范围在第一次访问时创建.

似乎订单大致如下:

> WebHostBuilder adds a startup filter to enable request services to be added to the pipeline.
>启动过滤器AutoRequestServicesStartupFilter将中间件添加到管道的最开头,以触发请求服务的创建.
>添加的中间件RequestServicesContainerMiddleware基本上只是从ServiceProvidersFeature调用RequestServices属性来触发创建每个请求的生命周期范围.但是,它用于创建请求范围的in its constructor is where it gets the IServiceScopeFactory,这不是很好,因为它将在可以建立租户之前从根容器创建.

所有这些都会导致已经确定每个请求范围是针对默认租户的情况,并且您无法真正更改它.

要解决此问题,您需要自己设置请求服务,以便它们考虑多租户.

听起来比实际情况更糟糕.

首先,我们需要对应用程序容器的引用.我们需要能够从应用程序级服务而不是请求服务来解析某些东西.我通过向您的Startup类添加静态属性并将容器保留在那里来做到这一点.

public static IContainer ApplicationContainer { get; private set; }

接下来,我们将更改您的中间件看起来更像RequestServicesContainerMiddleware.您需要先设置HttpContext,以便您的租户ID策略有效.之后,您可以获取IServiceScopeFactory并遵循它们在RequestServicesContainerMiddleware中执行的相同模式.

public class RequestMiddleware
{
  private static readonly AsyncLocal<HttpContext> _context = new AsyncLocal<HttpContext>();

  private readonly RequestDelegate _next;

  public RequestMiddleware(RequestDelegate next)
  {
    this._next = next;
  }

  public static HttpContext Context => _context.Value;

  public async Task Invoke(HttpContext context)
  {
    _context.Value = context;
    var existingFeature = context.Features.Get<IServiceProvidersFeature>();
    using (var feature = new RequestServicesFeature(Startup.ApplicationContainer.Resolve<IServiceScopeFactory>()))
    {
      try
      {
        context.Features.Set<IServiceProvidersFeature>(feature);
        await this._next.Invoke(context);
      }
      finally
      {
        context.Features.Set(existingFeature);
        _context.Value = null;
      }
    }
  }
}

现在,您需要一个启动过滤器来获取中间件.您需要一个启动过滤器,否则RequestServicesContainerMiddleware将在管道中运行得太早,并且已经开始从错误的租户范围开始解析.

public class RequestStartupFilter : IStartupFilter
{
  public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
  {
    return builder =>
    {
      builder.UseMiddleware<RequestMiddleware>();
      next(builder);
    };
  }
}

将启动筛选器添加到服务集合的最开头.您需要在AutoRequestServicesStartupFilter之前运行启动过滤器.

ConfigureServices最终看起来像这样:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  services.Insert(0, new ServiceDescriptor(typeof(IStartupFilter), typeof(RequestStartupFilter), ServiceLifetime.Transient));
  services.AddMvc();

  var builder = new ContainerBuilder();
  builder.RegisterType<TestMultitenancyContext>().InstancePerTenant();

  builder.Populate(services);
  var container = new MultitenantContainer(new MultitenantIdentificationStrategy(), builder.Build());
  ApplicationContainer = container;
  return new AutofacServiceProvider(container);
}

请注意插入呼叫,以便在启动过滤器之前将服务注册置于顶部.

新的运营顺序将是:

>在app启动时……

>您的启动过滤器会将您的自定义请求服务中间件添加到管道中.
> AutoRequestServicesStartupFilter将RequestServicesContainerMiddleware添加到管道.

>在请求期间……

>您的自定义请求中间件将根据入站请求信息设置请求服务.
> RequestServicesContainerMiddleware将看到已经设置了请求服务,并且什么都不做.
>解析服务后,请求服务范围已经是您的自定义请求中间件设置的租户范围,并且将显示正确的内容.

我通过将租户ID切换为来自查询字符串而不是主机名来本地测试这个(因此我没有设置主机文件条目和所有爵士乐)并且我能够通过切换查询字符串参数来切换租户.

现在,您可以稍微简化一下.例如,通过直接对Program类中的Web主机构建器执行某些操作,您可以在没有启动过滤器的情况下逃脱.您可以在调用builder.Populate并跳过该Insert调用之前,使用ContainerBuilder注册您的启动过滤器.如果您不希望Autofac在系统中传播,则可以将IServiceProvider存储在Startup类属性中.如果您创建中间件实例并自己将容器作为构造函数参数传递,则可以在没有静态容器属性的情况下离开.不幸的是,我已经花了很多时间试图找出解决方法所以我将不得不将“优化它”作为读者的练习.

再次,对不起,这不清楚. I’ve filed an issue on your behalf to get the docs updated and maybe figure out a better way to do this that’s a little more straightforward.

网友评论