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

Posted

技术标签:

【中文标题】如何将单个 IOptions POCO 交叉绑定到多个配置提供程序?【英文标题】:How to cross-bind single IOptions POCO to multiple configuration providers? 【发布时间】:2022-01-09 04:58:43 【问题描述】:

我是 .net 核心的新手,我在为单个 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&lt;ServerConfig&gt; 对象时,它只绑定数据库中的数据:

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 中填充了所有属性?

【问题讨论】:

【参考方案1】:

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

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

数据库

"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

【讨论】:

感谢您的回答。忘了说数据库属性也用在其他服务中,所以一般认为数据库是只读的。我想,我可以在 appsettings.json 中提取根级别的 ServerConfig 属性,所以我可以达到相同的最终结果,但是还有其他方法吗? @vanogrid 从数据库中读取设置并使用内存源将它们添加到配置中,以便可以在一个操作中进行绑定。 @vanogrid 您仍然可以创建一个自定义配置提供程序,该提供程序从数据库中提取设置并在填充配置时将层次结构添加到键中。 你是对的 - 它是一个自定义配置提供程序。我将尝试在此处添加层次结构。谢谢!

以上是关于如何将单个 IOptions POCO 交叉绑定到多个配置提供程序?的主要内容,如果未能解决你的问题,请参考以下文章

有啥理由将 POCO 变成 Model 对象?

如何将 ASP.NET FormView 绑定到单个记录?

如何使用 Knockout.js 将多个 View 绑定到单个 ViewModel

如何使用 c# 将单个 html 行绑定到 xamarin.forms 中的多个标签

将多个类的数据绑定到单个列表视图/xamarin 表单

数据绑定 POCO 属性