【问题标题】:How to load polymorphic objects in appsettings.json如何在 appsettings.json 中加载多态对象
【发布时间】:2019-09-21 02:49:44
【问题描述】:

有什么方法可以以强类型的方式从appsettings.json 读取多态对象?下面是我需要的一个非常简化的示例。

我有多个应用组件,在此处命名为 Features。这些组件由工厂在运行时创建。我的设计意图是每个组件都由其单独的强类型选项配置。在此示例中,FileSizeCheckerOptionsPersonCheckerOption 是这些的实例。每个功能都可以通过不同的选项多次包含。

但是使用现有的 ASP.NET Core 配置系统,我无法读取 多态 强类型选项。如果设置是由 JSON 反序列化器读取的,我可以使用 something like this。但appsettings.json 的情况并非如此,其中选项只是键值对。

appsettings.json

{
    "DynamicConfig":
    {
        "Features": [
            {
                "Type": "FileSizeChecker",
                "Options": { "MaxFileSize": 1000 }
            },
            {
                "Type": "PersonChecker",
                "Options": {
                    "MinAge": 10,
                    "MaxAge": 99
                }
            },
            {
                "Type": "PersonChecker",
                "Options": {
                    "MinAge": 15,
                    "MaxAge": 20
                }
            }
        ]
    }
}

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<FeaturesOptions>(Configuration.GetSection("DynamicConfig"));
        ServiceProvider serviceProvider = services.BuildServiceProvider();
        // try to load settings in strongly typed way
        var options = serviceProvider.GetRequiredService<IOptions<FeaturesOptions>>().Value;
    }

其他定义

public enum FeatureType
{
    FileSizeChecker,
    PersonChecker
}

public class FeaturesOptions
{
    public FeatureConfig[] Features { get; set; }
}

public class FeatureConfig
{
    public FeatureType Type { get; set; }
    // cannot read polymorphic object
    // public object Options { get; set; } 
}

public class FileSizeCheckerOptions
{
    public int MaxFileSize { get; set; }
}

public class PersonCheckerOption
{
    public int MinAge { get; set; }
    public int MaxAge { get; set; }

}

【问题讨论】:

  • 只做"FileSizeCheckerOptions": { "MaxFileSize": 1000 }会有缺点吗?
  • @ScottHannen 它不会。我需要读取一种特性(FileSizeChecker)——这是工厂创建新特性的关键。我需要为此功能加载选项。
  • 您找到解决方案了吗?
  • @HariPachuveetil 我认为没有简单的解决方案。这是由 .Core 加载机制引起的,其中所有值都被视为键值对,因此没有多态空间。

标签: c# json asp.net-core polymorphism json-deserialization


【解决方案1】:

回答这个问题的关键是要知道密钥是如何生成的。在您的情况下,键/值对将是:

DynamicConfig:Features:0:Type
DynamicConfig:Features:0:Options:MaxFileSize
DynamicConfig:Features:1:Type
DynamicConfig:Features:1:Options:MinAge
DynamicConfig:Features:1:Options:MaxAge
DynamicConfig:Features:2:Type
DynamicConfig:Features:2:Options:MinAge
DynamicConfig:Features:2:Options:MaxAge

注意数组的每个元素是如何由DynamicConfig:Features:{i} 表示的。

要知道的第二件事是,您可以使用ConfigurationBinder.Bind 方法将配置的任何部分映射到对象实例:

var conf = new PersonCheckerOption();
Configuration.GetSection($"DynamicConfig:Features:1:Options").Bind(conf);

当我们将所有这些放在一起时,我们可以将您的配置映射到您的数据结构:

services.Configure<FeaturesOptions>(opts =>
{
    var features = new List<FeatureConfig>();

    for (var i = 0; ; i++)
    {
        // read the section of the nth item of the array
        var root = $"DynamicConfig:Features:{i}";

        // null value = the item doesn't exist in the array => exit loop
        var typeName = Configuration.GetValue<string>($"{root}:Type");
        if (typeName == null)
            break;

        // instantiate the appropriate FeatureConfig 
        FeatureConfig conf = typeName switch
        {
            "FileSizeChecker" => new FileSizeCheckerOptions(),
            "PersonChecker" => new PersonCheckerOption(),
            _ => throw new InvalidOperationException($"Unknown feature type {typeName}"),
        };

        // bind the config to the instance
        Configuration.GetSection($"{root}:Options").Bind(conf);
        features.Add(conf);
    }

    opts.Features = features.ToArray();
});

注意:所有选项都必须派生自 FeatureConfig 才能正常工作(例如 public class FileSizeCheckerOptions : FeatureConfig)。您甚至可以使用反射自动检测从FeatureConfig 继承的所有选项,以避免切换类型名称。

注意 2:如果您愿意,您也可以将您的配置映射到 Dictionarydynamic 对象;看我对Bind netcore IConfigurationSection to a dynamic object的回答。

【讨论】:

  • 一个不费吹灰之力的问题的绝佳答案。
猜你喜欢
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 1970-01-01
  • 2020-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多