如何将 IHttpContextAccessor 注入 Autofac TenantIdentificationStrategy

How to inject IHttpContextAccessor into Autofac TenantIdentificationStrategy

我正在将我的多租户应用程序从 Webapi 迁移到 aspnet 核心。在 webapi 版本中,我使用 TenantIdentificationStrategy 来根据 HttpContext.

上的请求路径识别租户

转向 aspnet 核心,我能够成功连接 autofac。我无法弄清楚如何连接租户策略。我尝试在 ConfigureServices 中注入 IHttpContextAccessor 作为

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 

我的策略是这样的

public class AssetClassIdentificationStrategy: ITenantIdentificationStrategy {
    private readonly IHttpContextAccessor _accessor;
    public AssetClassIdentificationStrategy(IHttpContextAccessor httpContextAccessor)
    {
        _accessor = httpContextAccessor;
    }
    public bool TryIdentifyTenant(out object tenantId) {
        tenantId = null;
        var context = _accessor.HttpContext;
        if (context != null && context.Request != null )){
            var matchRegex = new Regex(@"\/[\d,\.,\w]*\/(\w*)\/.*");
            var match = matchRegex.Match(context.Request.Path.ToString());
            if (match.Success) {
                tenantId = match.Groups[1].Value.ToLower();
            }
        }
        return tenantId != null;
    }
}

我看到的是 HttpContextAccessor 被正确注入,其中 HttpContext 始终为空。因此,none 的多租户服务正在得到解决。

四处寻找示例,但找不到适合问题的内容。 Autofacv3 中曾经有一个 RequestParameterTenantIdentificationStrategy 不再支持。感谢对此的任何帮助。

编辑 修复了代码问题并按要求添加 Startup.cs。

public class Startup
{
    public Startup(IHostingEnvironment env) {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.Configure<CacheConfig>(Configuration.GetSection("Caching"),false);
        services.AddMvc();
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddTransient<ITenantIdentificationStrategy,AssetClassIdentificationStrategy>();

        var builder = new ContainerBuilder();
        builder.Populate(services);
        builder.RegisterType<TenantInfo>().WithProperty("TenantName", "unknown").As<ITenantInfo>();

        var container = builder.Build();

        ITenantIdentificationStrategy tenantIdentificationStrategy;
        bool isMultiTenant = container.TryResolve(out tenantIdentificationStrategy);

        var mtc = new MultitenantContainer(tenantIdentificationStrategy, container);
        mtc.ConfigureTenant("pesonalLoans", b => {
            b.RegisterType<TenantInfo>().WithProperty("TenantName","pesonalLoans") .As<ITenantInfo>();
        });
        mtc.ConfigureTenant("retirement", b => {
            b.RegisterType<TenantInfo>().WithProperty("TenantName", "retirement").As<ITenantInfo>();
        });

        return mtc.Resolve<IServiceProvider>();

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        LoggingConfig.Register(Configuration, loggerFactory);
        app.UseMvc();
    }
}


public class ValuesController : Controller {
    private ITenantInfo _tenant;
    public ValuesController(ITenantInfo tenant) {
        _tenant = tenant;
    }

    [HttpGet]
    public string Get()
    {
        return  _tenant.TenantName;
    }
}


public interface ITenantInfo {
    string TenantName { get; set; }
}
public class TenantInfo: ITenantInfo
{
    public string TenantName { get; set; }
}

编辑 3 project.json

{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0-rc2-3002702",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
    "Autofac": "4.0.0-rc2-240",
    "Autofac.Multitenant": "4.0.0-beta8-219",
    "System.IdentityModel.Tokens.Jwt": "5.0.0-rc2-305061149",
    "Autofac.Extensions.DependencyInjection": "4.0.0-rc2-240",
    "System.Reflection": "4.1.0-rc2-24027",
    "System.Reflection.Primitives": "4.0.1-rc2-24027",
    "System.Reflection.Extensions": "4.0.1-rc2-24027",
    "System.Reflection.TypeExtensions": "4.1.0-rc2-24027",
    "System.Reflection.Emit": "4.0.1-rc2-24027",
    "System.Reflection.Context": "4.0.1-rc2-24027",
    "System.Reflection.DispatchProxy": "4.0.1-rc2-24027",
    "System.Reflection.Emit.ILGeneration": "4.0.1-rc2-24027",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
    "Microsoft.AspNet.Mvc.Formatters.Xml": "6.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.Formatters.Json": "6.0.0-rc1-final",
  },

  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "dnxcore50",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "gcServer": true
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

评论太长了。

首先,您的 class 被命名为 SampleIdentificationStrategy,但您的构造函数引用了 AssetClassIdentificationStrategy。从这个问题开始,项目甚至不应该编译。

接下来(因为您还没有提供启动文件)确保通过在 ConfigureServices 方法中调用以下代码来填充 AutoFac 中的注册服务。

builder.Populate(services);
builder.Update(container);

请注意此方法必须 运行 您已将所有服务注册到 IServiceCollection

接下来确保您没有混用框架版本。 RC2 RC1beta-x 等之间存在很大差异。这已在 GitHub 问题日志中注明 here and here

除此之外,我们还需要查看您的 startup.cs 文件(特别是 ConfigureServices 方法的摘录,您的 project.json 文件(特别是 frameworksdependency 节点).

目前没有办法将东西注入到租户识别策略中,因为该策略本身不通过 DI 管道。

IHttpContextAccessor 通常仅由 HttpContextAccessor 支持,无论如何这是一个单例,并通过从 async/thread 本地上下文获取信息来执行操作。您可以在启动时直接使用其中之一来更新您的策略:

var strat = new MyStrategy(new HttpContextAccessor());

请注意,在最初提出问题时,多租户与 ASP.NET 核心 IServiceProvider 系统交互的方式存在问题,也就是说,它没有。

从那时起,我们发布了 4.0.0-rc3-309 for the Autofac.Extensions.DependencyInjection package 解决了这个问题。

变化是您需要将ConfigureServices更新为return new AutofacServiceProvider(mtc);,不再执行return mtc.Resolve<IServiceProvider>();