如何将单个 IOptions POCO 交叉绑定到多个配置提供程序?

How to cross-bind single IOptions POCO to multiple configuration providers?

我是 .net core 的新手,我在为单个 POCO 使用多个配置提供程序时遇到问题。我使用的是 .net 核心,而不是 asp.net 核心。

为了清楚起见,我试图简化 类,请忽略任何编译错误。我配置的型号如下:

public class Configurations
{
  public EnvironmentConfig EnvironmentConfig {get; set;}
  public ServerConfig ServerConfig {get; set;}
}

public class EnvironmentConfig
{
  public string EnvironmentStr {get; set;}
}

public class ServerConfig
{
  public string Address {get; set;}
  public string Username {get; set;}
  public string Password {get; set;}
}

我有两个提供商 - appsettings.json 和一个数据库。该数据库与其他服务一起使用,因此不可能轻易更改数据(它可以被认为是只读的)。 appsettings.json 文件具有以下层次结构:

{
  "EnvironmentConfig": {
    "EnvironmentStr": "dev"
  },
  "ServerConfig": {
    "Address": "https://example.com"
    // the other properties are not here, they're kept only in the database
  }
}

数据库没有层次结构,只提供缺少的属性(为了保持一致性,我将再次使用 JSON):

{
  "Username": "user"
  "Password": "password"
}

当我尝试获取 IOptionsMonitor<Configurations> 对象时,只有 Address 属性 填充在 ServerConfig 中,就像它只从 appsettings.json 读取一样(用户名和密码处于根级别,因此未正确绑定它们):

var configs = host.Services.GetService<IOptionsMonitor<Configurations>>().CurrentValue;

{
  "EnvironmentConfig": {
    "EnvironmentStr": "dev"
  },
  "ServerConfig": {
    "Address": "https://example.com"
    "Username": null,
    "Password": null
  }
}

当我尝试获取 IOptionsMonitor<ServerConfig> 对象时,它仅绑定来自数据库的数据:

var serverConfig = host.Services.GetService<IOptionsMonitor<ServerConfig>>().CurrentValue;

{
  "Address": null,
  "Username": "user",
  "Password": "password"
}

看来我没有正确绑定它。我尝试了多种方法,但没有用:

public static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration)
{
  services
    .Configure<ServerConfig>(configuration) // the db properties are also at root level
    .Configure<Configurations>(configuration);
  return serviceCollection;
}
public static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration)
{
  services
    .AddOptions<ServerConfig>()
    .Bind(configuration.GetSection("ServerConfig");
  services
    .AddOptions<Configurations>()
    .Bind(configuration);
  return serviceCollection;
}

有人可以解释如何正确绑定它,而不考虑层次结构的差异,所以 ServerConfigConfigurations 中填充了所有属性吗?

我假设数据库是作为自定义配置提供程序访问的

更新来自数据库的键以匹配所需的层次结构

数据库:

"ServerConfig:Username" -> "user"
"ServerConfig:Password" -> "password"

因此配置框架在合并来自多个配置提供程序的所有配置时知道您指的是什么。

从数据库中提取设置并在加载配置时将层次结构添加到键中。

简化示例:

public class ServerConfigProvider : ConfigurationProvider {
    public ServerConfigProvider (Action<DbContextOptionsBuilder> optionsAction) {
        OptionsAction = optionsAction;
    }

    Action<DbContextOptionsBuilder> OptionsAction { get; }

    public override void Load() {
        var builder = new DbContextOptionsBuilder<MyEFConfigurationContext>();

        OptionsAction(builder);

        using (var dbContext = new MyEFConfigurationContext(builder.Options)) {
            Data = dbContext.MySettingsTable.ToDictionary(c => $"ServerConfig:{c.Key}", c => c.Value);
        }
    }    
}

引用Custom configuration provider