aspof核心应用程序中的Autofac.Multitenant似乎无法正确解析租户作用域依赖项

时间:2016-08-14 07:52:04

标签: asp.net-core autofac

我正在升级使用Autofac.Multitenant框架的Multitenant dotnet核心解决方案。我没有很多运气让租约解决方案正常工作。我在这里创建了一个简单的问题演示:https://github.com/SaltyDH/AutofacMultitenancy1

此repo演示了在归属控制器中解析的InstancePerTenant作用域依赖TestMultitenancyContext。由于使用IHttpContextAccessor的问题,我使用自定义RequestMiddleware类来捕获当前的HttpContext对象,以便我可以在MultitenantIdentificationStrategy中的当前HttpContext请求对象上执行逻辑。

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

我在这里错过了什么,或者目前还没有工作?

1 个答案:

答案 0 :(得分:0)

我有一个替代解决方案,与我在Autofac DI扩展程序pending PR上完成的工作相关。那里的解决方案不能完全使用,因为它取决于(正确的)内部类。它可以通过提供在这些类中重现功能的垫片来进行调整。由于它们很紧凑,因此不需要添加大量代码。在功能修复之前,这是我正在使用的解决方案。

该解决方案的另一个方面是避开自定义中间件,而是使ITenantIdentificationStrategy服务可以采取任何必要的依赖来完成它所需的工作。

修复DI

" DI"问题的一面是Autofac DI扩展使用分辨率来提供IServiceProviderIServiceScopeFactory实现。这是可能的,因为在引擎盖下这些是IComponentContextILifetimeScope(它们本身是同一事物的不同接口)。在大多数情况下,这样可以正常工作,但ASP.NET Core会在应用程序周期的早期解析单个IServiceScopeFactory。在多租户方案中,此解决方案将为所请求的第一个租户或"默认"返回ILifetimeScope。租户,这将是应用程序生命周期的根范围(就MS DI而言)。 (有关进一步的讨论,请参阅PR。)

下面的类实现了一个替代行为:它不是解析DI接口,而是直接从new构建(IContainer s-up)最初请求的接口。最初的IServiceScopeFactory直接基于IContainer,进一步的范围请求将正确解析。

public class ContainerServiceProvider : IServiceProvider, ISupportRequiredService
{
  private readonly IContainer container;

  public ContainerServiceProvider(IContainer container)
  {
    this.container = container;
  }

  public object GetRequiredService(Type serviceType)
  {
    if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
    else return container.Resolve(serviceType);
  }

  public object GetService(Type serviceType)
  {
    if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
    else return container.ResolveOptional(serviceType);
  }

  bool TryGetContainer(Type serviceType, out object containerSvc)
  {
    if (serviceType == typeof(IServiceProvider)) { containerSvc = this; return true; }
    if (serviceType == typeof(IServiceScopeFactory)) { containerSvc = new ContainerServiceScopeFactory(container); return true; }
    else { containerSvc = null; return false; }
  }
}

// uses IContainer, but could use copy of AutofacServiceScopeFactory
internal class ContainerServiceScopeFactory : IServiceScopeFactory
{
  private IContainer container;

  public ContainerServiceScopeFactory(IContainer container)
  {
    this.container = container;
  }

  public IServiceScope CreateScope()
  {
    return new BecauseAutofacsIsInternalServiceScope(container.BeginLifetimeScope());
  }
}

// direct copy of AutofacServiceScope
internal class BecauseAutofacsIsInternalServiceScope : IServiceScope
{
  private readonly ILifetimeScope _lifetimeScope;

  /// <summary>
  /// Initializes a new instance of the <see cref="AutofacServiceScope"/> class.
  /// </summary>
  /// <param name="lifetimeScope">
  /// The lifetime scope from which services should be resolved for this service scope.
  /// </param>
  public BecauseAutofacsIsInternalServiceScope(ILifetimeScope lifetimeScope)
  {
    this._lifetimeScope = lifetimeScope;
    this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>();
  }

  /// <summary>
  /// Gets an <see cref="IServiceProvider" /> corresponding to this service scope.
  /// </summary>
  /// <value>
  /// An <see cref="IServiceProvider" /> that can be used to resolve dependencies from the scope.
  /// </value>
  public IServiceProvider ServiceProvider { get; }

  /// <summary>
  /// Disposes of the lifetime scope and resolved disposable services.
  /// </summary>
  public void Dispose()
  {
    this._lifetimeScope.Dispose();
  }
}

修复识别策略

至于将识别策略作为服务,我会像你这样重写你的实现:

public class MultitenantIdentificationStrategy : ITenantIdentificationStrategy
{
  public const string DefaultTenantId = null;
  private readonly IHttpContextAccessor contextaccessor;

  public MultitenantTenantIdentificationStrategy(IHttpContextAccessor contextaccessor)
  {
    this.contextaccessor = contextaccessor;
  }

  public bool TryIdentifyTenant(out object tenantId)
  {
    var context = contextaccessor.HttpContext;
    // after this is unchanged
    .
    .

  }
  .
  .

}

在Startup.ConfigureServices

中使用

这显示了这些最后几个部分如何注册并提供给ASP.NET的MS DI的片段。

. . .
builder.RegisterType<MultitenantIdentificationStrategy>().AsImplementedInterfaces();   // tenant identification

// register do Autofac DI integration
builder.Populate(services);

var underlyingcontainer = builder.Build();
ApplicationContainer = new MultitenantContainer(underlyingcontainer.Resolve<ITenantIdentificationStrategy>(), underlyingContainer);

return new ContainerServiceProvider(ApplicationContainer);

如果您发现此解决方案可行,请赞同DI PR 10 - 或PR 11,如果在审核后您认为这是更好/更优雅的解决方案。要么省去必须添加&#34; shim&#34;上面的代码。