Autofac:多租户和ASP.NET Core 3.1

问题描述 投票:0回答:2

我在使多租户工作时遇到问题。我尝试遵循示例here,但看不到我的实现在做什么。

租户由地址字段中的路由参数标识。这似乎可以正常工作(调用TryIdentifyTenant返回正确的代码)。我正在使用ASP.NET Core 3.1,以及Autofac.AspNetCore-Multitenant v3.0.1和Autofac.Extensions.DependencyInjection v6.0.0。

我简化了代码(已经过测试,但仍然无法正常工作)。配置了两个租户,即“ terminal1”和“ terminal2”。输出应根据租户而有所不同。但是,它总是返回基本实现。在下面的示例中,输入“ https://localhost/app/terminal1”将返回“ base:terminal1”,而“ https://localhost/app/terminal2”将返回“ base:terminal2”。它应该返回“ userhandler1:terminal1”和“ userhandler2:terminal2”。

HomeController:

     public class HomeController : Controller
    {
        private readonly IUserHandler userHandler;
        private readonly TerminalResolverStrategy terminalResolverStrategy;

        public HomeController(IUserHandler userHandler, TerminalResolverStrategy terminalResolverStrategy)
        {
            this.userHandler = userHandler;
            this.terminalResolverStrategy = terminalResolverStrategy;
        }

        public string Index()
        {
            terminalResolverStrategy.TryIdentifyTenant(out object tenant);
            return userHandler.ControllingVncUser + " : " + (string)tenant;
        }
    }

UserHandler:

public interface IUserHandler
    {
        public string ControllingVncUser { get; set; }
    }

    public class UserHandler : IUserHandler
    {
        public UserHandler()
        {
            ControllingVncUser = "base";
        }

        public string ControllingVncUser { get; set; }
    }


    public class UserHandler1 : IUserHandler
    {
        public UserHandler1()
        {
            ControllingVncUser = "userhandler1";
        }

        public string ControllingVncUser { get; set; }
    }

    public class UserHandler2 : IUserHandler
    {
        public UserHandler2()
        {
            ControllingVncUser = "userhandler2";
        }

        public string ControllingVncUser { get; set; }
    }

启动:

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

        public IConfiguration Configuration { get; }

           public void ConfigureServices(IServiceCollection services)
        {

            services.AddHttpContextAccessor();

            services.AddControllersWithViews();
            services.AddAutofacMultitenantRequestServices();
        }

        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterType<TerminalResolverStrategy>();
            builder.RegisterType<UserHandler>().As<IUserHandler>();
        }

        public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
        {
            var strategy = new TerminalResolverStrategy(
                container.Resolve<IOptions<TerminalAppSettings>>(), 
                container.Resolve<IHttpContextAccessor>());

            var mtc = new MultitenantContainer(strategy, container);

            mtc.ConfigureTenant("terminal1", b => b.RegisterType<UserHandler1>().As<IUserHandler>());
            mtc.ConfigureTenant("terminal2", b => b.RegisterType<UserHandler2>().As<IUserHandler>());

            return mtc;
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            loggerFactory.AddLog4Net();

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();


            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{terminal}/{controller=Home}/{action=Index}/{id?}");
            });
        }
}

程序:

        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>();
                });
    }

ITenantIdentificationStrategy:

        public class TerminalResolverStrategy : ITenantIdentificationStrategy
    {
        public IHttpContextAccessor Accessor { get; private set; }

        private readonly TerminalAppSettings settings;

        public TerminalResolverStrategy(
            IOptions<TerminalAppSettings> options,
            IHttpContextAccessor httpContextAccessor
            )
        {
            Accessor = httpContextAccessor;
            settings = options.Value;
        }

        public bool TryIdentifyTenant(out object terminal)
        {
            HttpContext httpCtx = Accessor.HttpContext;//
            terminal = null;
            try
            {
                if (httpCtx != null &&
                    httpCtx.Request != null &&
                    httpCtx.Request.RouteValues != null &&
                    httpCtx.Request.RouteValues.ContainsKey("terminal"))
                {
                    string requestedTerminal = httpCtx.Request.RouteValues["terminal"].ToString();
                    bool terminalExists = settings.Terminals.ContainsKey(requestedTerminal);
                    if (terminalExists)
                    {
                        terminal = requestedTerminal;
                    }
                }
            }
            catch (Exception) {}
            return terminal != null;
        }
    }
}

我在做什么错?预先感谢。

asp.net-core autofac multi-tenant asp.net-core-3.1
2个回答
0
投票

“多租户似乎根本不起作用”是一个模棱两可的陈述,很难解决。不幸的是,我没有时间[[个人下载您所有的示例代码,并尝试重新编写整个代码并对其进行调试,以确切地了解出了什么问题。也许有人这样做。但是,我可以提供一些提示,例如要看的地方以及要尝试查看的内容。

Tenant ID策略设置两次。我在Startup.ConfigureContainer中看到了builder.RegisterType<TerminalResolverStrategy>();行-这会将您的策略​​类型注册为每个实例的实例,因此每次需要时解决新鲜。我还在Startup.ConfigureMultitenantContainer中看到您正在手动实例化多租户容器使用的策略。发生混乱的可能性不为零。我会选择一种方法来完成此任务-注册策略或手动创建策略-并且我将确保该方法是无状态单例。 (未在示例中注册。)

路由模式可能有问题。

我看到您注册的路由模式如下:{terminal}/{controller=Home}/{action=Index}/{id?}。我还看到您的URL如下所示:https://localhost/app/terminal1您是否已进入租户ID策略以确保路由解析机制正常工作?也就是说,app不会被当作terminal值吗?路由解析/处理可能很棘手。

可能是错误的设置。

仅当存在指定存在特定terminal值的选项时,租户ID策略才能成功识别租户。我看不到这些选项中的任何一个配置,这意味着此仓库中未定义任何租户。如果没有这一点,您的策略将无法识别任何东西。如果是我,我可能会从该租户ID策略的一个断点开始,看看解决了什么,没有解决什么。从外部角度看,这似乎有些复杂,这就是我要开始的地方。如果这有效,那么我可能还会考虑清理注册,以便ID策略不会被两次注册。最终,我得到的印象不是这是应用程序中的所有代码;而是我可能会考虑制作一个极小的复制品,其大小与您实际在此处发布的大小差不多。然后,我将集中精力进行最少的复制工作。然后,一旦工作,我就会弄清楚repro和我的大型应用之间有什么区别。

不幸的是,这就是我所能提供的。如前所述,在当前环境和当前工作量的情况下,我将无法坐下来用您的代码重现整个过程。我知道集成有效,因为我有生产应用正在使用它。并且有很多测试(单元和集成)来验证它是否有效;因此不起作用的部分可能在您的代码中某处...而这些正是我要开始的地方。


0
投票
因此,在将ITenantStrategy确定为问题之后,似乎RouteValues直到请求链中的稍后阶段都无法通过HttpContext解决。因此,没有租户得到解决。在我看来,这就像.NET Core中的错误。通过使用请求路径来绕过问题:

public class TerminalResolverStrategy : ITenantIdentificationStrategy { private readonly TerminalAppSettings settings; private readonly IHttpContextAccessor httpContextAccessor; public TerminalResolverStrategy( IOptions<TerminalAppSettings> options, IHttpContextAccessor httpContextAccessor ) { this.httpContextAccessor = httpContextAccessor; settings = options.Value; } public bool TryIdentifyTenant(out object terminal) { var httpCtx = httpContextAccessor.HttpContext; terminal = null; try { if (httpCtx != null) { string thisPath = httpCtx.Request.Path.Value; var allTerminals = settings.Terminals.GetEnumerator(); while (allTerminals.MoveNext()) { if (thisPath.Contains(allTerminals.Current.Key)) { terminal = allTerminals.Current.Key; return true; } } } } catch (Exception) { } return false; } }

© www.soinside.com 2019 - 2024. All rights reserved.