简体   繁体   English

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

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

I'm new to .net core and I have a problem with using multiple configuration providers for a single POCO.我是 .net 内核的新手,我在为单个 POCO 使用多个配置提供程序时遇到问题。 I'm using .net core, not asp.net core.我使用的是 .net 内核,而不是 asp.net 内核。

I tried to simplify the classes for clarity, please ignore any compilation errors.为了清楚起见,我试图简化类,请忽略任何编译错误。 The model for my configuration is as follows:我的配置的model如下:

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;}
}

I have two providers - appsettings.json and a database.我有两个提供程序 - appsettings.json和一个数据库。 The database is used with other services, so it's not possible to alter the data easily (it can be considered read-only).该数据库与其他服务一起使用,因此无法轻松更改数据(可以认为是只读的)。 The appsettings.json file has the following hierarchy: appsettings.json文件具有以下层次结构:

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

The database doesn't have hierarchy and provides only the missing properties (I'll use JSON again for consistency):数据库没有层次结构,只提供缺失的属性(我将再次使用 JSON 以保持一致性):

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

When I try to get a IOptionsMonitor<Configurations> object, only the Address property is populated inside ServerConfig , like it's only reading from appsettings.json (username and password are at the root level, so it's not binding them correctly):当我尝试获取IOptionsMonitor<Configurations> object 时,仅在ServerConfig中填充了Address属性,就像它仅从appsettings.json读取一样(用户名和密码位于根级别,因此未正确绑定它们):

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

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

And when I try to get a IOptionsMonitor<ServerConfig> object, it binds only the data from the database:当我尝试获取IOptionsMonitor<ServerConfig> object 时,它只绑定数据库中的数据:

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

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

It looks like I'm not binding it correctly.看起来我没有正确绑定它。 I tried multiple ways, but it didn't work:我尝试了多种方法,但没有奏效:

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;
}

Could someone explain how to bind it correctly, regardless of the difference in hierarchy, so ServerConfig has all properties populated when inside Configurations ?有人可以解释如何正确绑定它,而不管层次结构的差异如何,因此ServerConfigConfigurations中填充了所有属性?

I am assuming the database is accessed as a custom configuration provider我假设数据库作为自定义配置提供程序访问

Update the keys coming from the database to match the desired hierarchy更新来自数据库的键以匹配所需的层次结构

Database :数据库

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

so the configuration framework knows what you are referring to when it merges all the configuration from the multiple configuration providers.所以配置框架在合并来自多个配置提供程序的所有配置时知道您指的是什么。

Extract the settings from the database and prepends the hierarchy to the keys when loading configuration.在加载配置时,从数据库中提取设置并将层次结构添加到键中。

Simplified example:简化示例:

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);
        }
    }    
}

Reference Custom configuration provider参考自定义配置提供程序

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM