Jon*_*Jon 7 c# configuration multi-tenant asp.net-core
我有一个多租户 ASP.NET Core Web 应用程序。当前的租户模型是每个租户都有一个单独的 Web 应用程序和 SQL 数据库。我正在尝试重新构建它,以便单个 Web 应用程序为多个租户提供服务(但为每个租户维护一个单独的数据库)。我一直在关注这一系列的博客文章,但在配置方面遇到了一些障碍。
该应用大量使用 ASP.NET Core 配置系统,并有一个自定义 EF Core 提供程序,可从数据库中获取配置值。如果可能的话,我想保留它,撕掉并替换为其他东西(在数百个地方使用了数十个配置设置)将是一项艰巨的工作。
现有代码非常标准:
public class MyAppSettings
{
public string FavouriteColour { get; set; }
public int LuckyNumber { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<MyAppSettings>(Configuration.GetSection("MyAppSettings"));
// etc....
}
}
// custom EF Core config provider wired up in Program.Main, but that doesn't actually seem relevant
Run Code Online (Sandbox Code Playgroud)
我已经更新了我们的自定义提供程序,以便它从所有已知的租户数据库中获取所有配置值,并将它们全部添加到配置系统中,并以租户标识符为前缀,因此从 n 个不同数据库中获取的所有配置值的列表可能看起来像这样:
Key Value
===============================================
TenantABC:MyAppSettings:FavouriteColour Green
TenantABC:MyAppSettings:LuckyNumber 42
TenantDEF:MyAppsettings:FavouriteColour Blue
TenantDEF:MyAppSettings:LuckyNumber 37
...
TenantXYZ:MyAppSettings:FavouriteColour Yellow
TenantXYZ:MyAppSettings:LuckyNumber 88
Run Code Online (Sandbox Code Playgroud)
我希望能够做的是以某种方式自定义绑定配置的方式,以便它解析当前请求的租户,然后使用适当的值,例如 abc.myapp.com 上的请求将观察配置值“绿色”和“42”等,而不必更改注入IOptionsMonitor<AppSettings>(或IOptionsSnapshot等)的所有依赖位置。链接的博客系列有一篇关于配置的文章,其中涵盖了一些我希望我最终会在缓存等方面遇到的问题,但它似乎无法满足为不同租户使用完全不同设置的这种情况。从概念上讲,它似乎很简单,但我一直无法找到正确的挂钩位置。请帮忙!
这是一个想法(但是尚未测试)。您可以保存传递给 Startup 类的构造函数的默认 IConfiguration 实例,然后在 DI 中注册您自己的 IConfiguration 实现,该实现将使用该默认实例和 HttpContextAccessor(以获取当前租户)。
所以代码看起来像:
public class Startup
{
private IConfiguration _defaultConfig;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
_defaultConfig = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
. . . .
services.AddScoped<IConfiguration>(serviceProvider => {
var httpContextAccessor =
serviceProvider.GetService<IHttpContextAccessor>();
return new MyConfig(_defaultConfig, httpContextAccessor);
});
}
. . . .
}
public class MyConfig : IConfiguration
{
private readonly IConfiguration _defaultConfig;
private readonly IHttpContextAccessor _httpContextAccessor;
public MyConfig(IConfiguration defaultConfig, IHttpContextAccessor httpContextAccessor)
{
_defaultConfig = defaultConfig;
_httpContextAccessor = httpContextAccessor;
}
public string this[string key] {
get {
var tenantId = GetTenantId();
return _defaultConfig[tenantId + ":" + key];
}
set {
var tenantId = GetTenantId();
_defaultConfig[tenantId + ":" + key] = value;
}
}
protected virtual string GetTenantId()
{
//this is just an example that supposes that you have "TenantId" claim associated with each user
return _httpContextAccessor.HttpContext.User.FindFirst("TenantId").Value; ;
}
public IEnumerable<IConfigurationSection> GetChildren()
{
return _defaultConfig.GetChildren();
}
public IChangeToken GetReloadToken()
{
return _defaultConfig.GetReloadToken();
}
public IConfigurationSection GetSection(string key)
{
var tenantId = GetTenantId();
return _defaultConfig.GetSection(tenantId + ":" + key);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
660 次 |
| 最近记录: |