Sal*_*lty 4 autofac asp.net-core
我正在升级使用Autofac.Multitenant框架的Multitenant dotnet核心解决方案.我没有很多运气让租约解决方案正常工作.我在这里创建了一个简单的问题演示:https://github.com/SaltyDH/AutofacMultitenancy1
此repo演示了如何注册在Home Controller中解析的InstancePerTenant作用域依赖项TestMultitenancyContext.由于使用IHttpContextAccessor的问题,我使用自定义RequestMiddleware类来捕获当前的HttpContext对象,以便我可以在当前的HttpContext请求对象上执行逻辑MultitenantIdentificationStrategy.
最后,TestFixture提供一个简单的xUnit测试,至少在我的机器上为两个租户返回"tenant1".
有没有我在这里错过的或者这只是目前没有工作?
更新10/6/2017:我们发布了Autofac.AspNetCore.Multitenant,以便在更易于使用的软件包中完成解决方案.我会在这里留下原始答案/解释给后人,但是如果你打这个,你可以抓住那个包然后继续前进.
我认为你遇到了时间问题.
如果你在中间件的HttpContext上弹出调试器,你会发现RequestServicesFeature在一个名为的属性上有一个对象ServiceProvidersFeature.这就是创建每个请求范围的责任.范围在第一次访问时创建.
似乎订单大致如下:
WebHostBuilder添加了启动过滤器,使请求的服务被添加到管道.AutoRequestServicesStartupFilter将中间件添加到管道的最开头,以触发请求服务的创建.RequestServicesContainerMiddleware,基本上只是调用RequestServices属性ServiceProvidersFeature来触发创建每个请求的生命周期范围.但是,在它的构造函数中,它IServiceScopeFactory用于创建请求范围,它不是那么好,因为它将在根据容器创建之前创建租户.所有这些都会导致已经确定每个请求范围是针对默认租户的情况,并且您无法真正更改它.
要解决此问题,您需要自己设置请求服务,以便它们考虑多租户.
听起来比实际情况更糟糕.
首先,我们需要对应用程序容器的引用.我们需要能够从应用程序级服务而不是请求服务来解析某些东西.我通过向static您的Startup类添加属性并将容器保留在那里来做到这一点.
public static IContainer ApplicationContainer { get; private set; }
Run Code Online (Sandbox Code Playgroud)
接下来,我们将更改您的中间件看起来更像是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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您需要一个启动过滤器来获取中间件.您需要一个启动过滤器,否则RequestServicesContainerMiddleware将在管道中运行太早,并且事情已经开始从错误的租户范围解决.
public class RequestStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder.UseMiddleware<RequestMiddleware>();
next(builder);
};
}
}
Run Code Online (Sandbox Code Playgroud)
将启动筛选器添加到服务集合的最开头.您需要先运行启动过滤器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);
}
Run Code Online (Sandbox Code Playgroud)
请注意其中的Insert呼叫在启动过滤器之前将服务注册置于顶部.
新的运营顺序将是:
AutoRequestServicesStartupFilter将添加RequestServicesContainerMiddleware到管道.RequestServicesContainerMiddleware会看到,请求服务已经建立,并会做什么.我通过将租户ID切换为来自querystring而不是主机名来本地测试这个(因此我没有设置主机文件条目和所有爵士乐)并且我能够通过切换查询字符串参数来切换租户.
现在,您可以稍微简化一下.例如,通过直接对类中的Web主机构建器执行某些操作,您可以在没有启动过滤器的情况下离开Program.您可以使用ContainerBuilder之前的呼叫注册您的启动过滤器builder.Populate并跳过该Insert呼叫.如果您不希望Autofac在系统中传播,您可以将其存储IServiceProvider在Startupclass属性中.如果您创建中间件实例并自己将容器作为构造函数参数传递,则可以在没有静态容器属性的情况下离开.不幸的是,我已经花了很多时间试图找出解决方法所以我将不得不将"优化它"作为读者的练习.
再次,对不起,这不清楚.我已经代表您提交了一个问题,以便更新文档,并且可能找出更好的方法来实现这一点,这更加简单明了.
| 归档时间: |
|
| 查看次数: |
1718 次 |
| 最近记录: |