我需要为现有系统构建Web API。全国各地都有各种机构,每个机构都有自己的数据库。所有数据库都在一台服务器上。所有数据库的结构都相同。所有数据库都有自己的用户名和密码。代理商拥有一个或多个用户。用户可以属于一个或多个代理商。还有一个特殊的数据库,其中包含所有用户的表格,所有代理商的表格以及用户代理商桥牌表。
目前,他们正在使用传统的Windows桌面应用程序。当用户设置此Windows程序时,他们使用用户名和密码登录。系统然后为他们显示他们所属的所有代理商的列表(通常只有一个,但有些"高级用户"可能属于少数)。他们选择一个代理商,然后程序连接到正确的数据库。对于会话的剩余部分,用户所做的一切都将在该数据库上完成。
客户想要创建一个Web应用程序以最终替换Windows程序(这两个程序将并排运行一段时间)。一位开发人员正在Angular 5中创建前端,我正在ASP .Net Core 2.1中开发API。
因此,网络应用程序的运行方式与Windows应用程序类似。用户登录Web应用程序。使用我的Web API的Web应用程序告诉API哪个用户刚刚登录。然后,API会从存储该数据的数据库中检查该用户所属的代理商。 API将用户所属的代理商列表返回给Web应用程序。在那里,用户选择一个代理商。从此时起,Web应用程序将在所有API调用的标头中包含此代理商ID。 API在收到来自Web应用程序的请求时,将根据请求标题中的代理商ID知道要使用的数据库。
希望有道理......
显然这意味着我必须动态更改DbContext的连接字符串,具体取决于API必须与之通信的数据库。我一直在研究这个问题,首先是在控制器本身上进行,但是在我的所有控制器中都会有很多复制粘贴反模式。所以我试图将其转移到DbContext的OnConfiguring事件。我认为最好使用适当的连接字符串创建DbContext Factory来创建DbContexts。我虽然有点迷失。您会看到,当Web应用程序在Web api上调用终点时(让我们说HTTP GET请求获取帐户列表),这将触发Accounts控制器中的HttpGet处理程序。然后,此操作方法将读取代理商ID标头。但这一切都发生在控制器上....如果我从DbContext的OnConfiguring()事件中调用DbContext Factory,它必须将代理ID(在控制器中读取)发送到工厂,以便工厂知道要创建哪个连接字符串。我试图不使用全局变量来保持我的类松散耦合。
除非我在管道中运行一些拦截所有请求的服务,否则读取代理ID标头,这会以某种方式注入DbContext构造函数?不知道我会怎么做...
总之,我有点迷失了。我甚至不确定这是否是正确的方法。我看过一些"多租户"例子,但说实话,我发现它们有点难以理解,我希望我现在可以做一些更简单的事情,随着时间的推移,随着我对.Net Core知识的改进,我可以看一下相应地改进代码。
答案 0 :(得分:2)
我正在研究你在这里描述的类似事情。由于我也很开始,我还没有银弹。有一件事可以帮助你的方法:
首先在控制器本身上执行此操作,该控制器工作但在我的所有控制器中都会涉及大量复制和粘贴反模式。
我采用了让中间件负责交换dbconnection字符串的方法。像这样:
public class TenantIdentifier
{
private readonly RequestDelegate _next;
public TenantIdentifier(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext, GlobalDbContext dbContext)
{
var tenantGuid = httpContext.Request.Headers["X-Tenant-Guid"].FirstOrDefault();
if (!string.IsNullOrEmpty(tenantGuid))
{
var tenant = dbContext.Tenants.FirstOrDefault(t => t.Guid.ToString() == tenantGuid);
httpContext.Items["TENANT"] = tenant;
}
await _next.Invoke(httpContext);
}
}
public static class TenantIdentifierExtension
{
public static IApplicationBuilder UseTenantIdentifier(this IApplicationBuilder app)
{
app.UseMiddleware<TenantIdentifier>();
return app;
}
}
这里我使用一个名为X-Tenant-Guid
的自创式http标头来识别租户GUID。然后我向全局数据库发出请求,在那里我获得了这个租户数据库的连接字符串。
我在这里公开了这个例子。 https://github.com/riscie/ASP.NET-Core-Multi-Tenant-multi-db-Example(它还没有更新到asp net core 2.1,但这样做不应该是一个问题)