我在使多租户工作时遇到问题。我尝试遵循示例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;
}
}
}
我在做什么错?预先感谢。
“多租户似乎根本不起作用”是一个模棱两可的陈述,很难解决。不幸的是,我没有时间[[个人下载您所有的示例代码,并尝试重新编写整个代码并对其进行调试,以确切地了解出了什么问题。也许有人这样做。但是,我可以提供一些提示,例如要看的地方以及要尝试查看的内容。
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和我的大型应用之间有什么区别。不幸的是,这就是我所能提供的。如前所述,在当前环境和当前工作量的情况下,我将无法坐下来用您的代码重现整个过程。我知道集成有效,因为我有生产应用正在使用它。并且有很多测试(单元和集成)来验证它是否有效;因此不起作用的部分可能在您的代码中某处...而这些正是我要开始的地方。
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;
}
}