【发布时间】:2021-02-15 16:04:32
【问题描述】:
我开始失去理智了:
我正在尝试构建一个 Blazor 应用程序,其中最终用户将连接到一个或另一个数据库,具体取决于他们访问应用程序的主机名。
例如,subdomain1.application.com 将连接到一个数据库,而 subdomain2.application.com 将连接到另一个数据库。我相信这个原则叫做 Mutlitenancy(?)。
为了实现这一点,我构建了一个“主”数据库,用于存储不同数据库的主机名和连接字符串。然后我有一个TenantService 类,它加载不同的连接并使用IHttpContextAccessor 通过注入返回与当前基本URI 对应的连接字符串。在调试中一切正常。
当我尝试在 Azure 上托管我的应用程序时,出现了我面临的问题。 IHttpContextAccessor.HttpContext 为空,因此我无法访问基本 URI。我在多个线程上读到 HttpContext 不存在于 SignalR 中,也不应该与 Blazor 服务器端一起使用。
我尝试过的事情:
- 将
NavigationManager注入我的TenantService,但出现异常InvalidOperationException: 'RemoteNavigationManager' has not been initialized
我看到人们谈论 SignalR 集线器来访问上下文,但我无法理解它是如何工作的。
如果有人构建了类似的东西,我会全力以赴寻找更好的方法。也许我需要重新开始,根本不使用基于 url 的多租户。感谢任何人的帮助。
编辑:这里有更多关于我今天如何实现它的细节。
TenantHolder.cs
public class TenantHolder : ITenantHolder
{
private List<Tenants> _tenants;
public TenantHolder(IServiceScopeFactory serviceScopeFactory)
{
using (var scope = serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var context = provider.GetRequiredService<MasterContext>())
_tenants = context.Tenants.ToList(); // Retrieve all the existing tenants from the MasterContext
// which is permanantly connected to a master database
}
}
public string GetCurrentTenant(HttpContext context)
{
var hostname = context.Request.Host.Value;
var tenant = _tenants.FirstOrDefault(x => x.Url == hostname);
return tenant.ConnectionString;
}
}
TenantService.cs
public class TenantService : ITenantService
{
private readonly HttpContext _httpContext;
private readonly ITenantHolder _tenantHolder;
public TenantService(IHttpContextAccessor accessor, ITenantHolder tenantHolder)
{
_httpContext = accessor.HttpContext; // Works fine in local but NULL when hosted on Azure
_tenantHolder = tenantHolder;
}
public string GetCurrentTenant()
=> _tenantHolder.GetCurrentTenant(_httpContext);
}
TenantContext.cs
public class TenantContext : IdentityDbContext<ApplicationUser> // Shorten
{
private readonly ITenantService _tenantService;
public TenantContext(DbContextOptions<TenantContext> options, ITenantService tenantService)
: base(options)
{
_tenantService = tenantService;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connectionString = _tenantService.GetCurrentTenant();
if (string.IsNullOrEmpty(connectionString)) ; // TODO: throw an exception or something
optionsBuilder.UseSqlServer(connectionString);
}
}
Startup.cs
services.AddDbContext<TenantContext>(opts => opts.UseSqlServer(masterConnectionString)); // Connected to the master database before the tenant management changes it
services.AddDbContext<MasterContext>(opts => opts.UseSqlServer(masterConnectionString));
services.AddHttpContextAccessor();
services.AddSingleton<ITenantHolder, TenantHolder>();
services.AddScoped<ITenantService, TenantService>();
【问题讨论】:
-
您只能将
NavigationManager注入 Razor 组件。当你打电话给TenantService时,你不能转发吗? -
不确定这是否有帮助,因为我对您的详细信息有点模糊,但是,Net5 中有一个
DbContextFactory的概念,它为您的数据库连接提供了很大的灵活性。这完全取决于您使用 DbContext 做什么。数据库是不同的结构,因此具有不同的模型,还是完全相同的结构,因此共享相同的模型。更改上下文,相同的模型适合所有人。如果是这样,那么您只需要根据站点获取正确的连接字符串。连接字符串可以存在于 appsettings 中。如果您需要更多帮助,请给我加分。 -
如果有帮助,这里有一个 sn-p: var dbContext = configuration.GetValue
("Configuration:DBContext"); services.AddDbContextFactory (options => options.UseSqlServer(dbContext), ServiceLifetime.Singleton); -
@ShaunCurtis 抱歉,如果我没有提供足够的详细信息。基本上我需要读取最终用户访问网站的基本 uri 并相应地连接到数据库。我在帖子中添加了一些细节以澄清问题。
-
@JHBonarius 你说的“前锋”是什么意思?
标签: blazor multi-tenant blazor-server-side