如何从.net core中的appsettings.json中提取列表

How to extract a list from appsettings.json in .net core

我有一个 appsettings.json 文件,如下所示:

{
    "someSetting": {
        "subSettings": [
            "one",
            "two",
            "three"
         ]
    }
}

当我构建我的配置根目录并执行类似 config["someSetting:subSettings"] 的操作时 returns null 并且可用的实际设置如下所示:

config["someSettings:subSettings:0"]

是否有更好的方法来检索 someSettings:subSettings 的内容作为列表?

您可以使用配置绑定器来获取配置源的强类型表示。

这是我之前写的一个测试的例子,希望对你有帮助:

    [Fact]
    public void BindList()
    {
        var input = new Dictionary<string, string>
        {
            {"StringList:0", "val0"},
            {"StringList:1", "val1"},
            {"StringList:2", "val2"},
            {"StringList:x", "valx"}
        };

        var configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.AddInMemoryCollection(input);
        var config = configurationBuilder.Build();

        var list = new List<string>();
        config.GetSection("StringList").Bind(list);

        Assert.Equal(4, list.Count);

        Assert.Equal("val0", list[0]);
        Assert.Equal("val1", list[1]);
        Assert.Equal("val2", list[2]);
        Assert.Equal("valx", list[3]);
    }

重要的部分是对 Bind 的调用。

测试和更多例子在GitHub

假设您的 appsettings.json 看起来像这样:

{
  "foo": {
    "bar": [
      "1",
      "2",
      "3"
    ]
  }
}

您可以像这样提取列表项:

Configuration.GetSection("foo:bar").Get<List<string>>()

在 .NetCore 中,这是我所做的:

正常设置:

在您的 appsettings.json 中为您的自定义定义创建一个配置部分:

    "IDP": [
    {
      "Server": "asdfsd",
      "Authority": "asdfasd",
      "Audience": "asdfadf"
    },
    {
      "Server": "aaaaaa",
      "Authority": "aaaaaa",
      "Audience": "aaaa"
    }
  ]

创建一个 class 来建模对象:

public class IDP
{
    public String Server { get; set; }
    public String Authority { get; set; }
    public String Audience { get; set; }

}

在你的 Startup -> ConfigureServices

services.Configure<List<IDP>>(Configuration.GetSection("IDP"));

Note: if you need to immediately access your list within your ConfigureServices method you can use...

var subSettings = Configuration.GetSection("IDP").Get<List<IDP>>();

然后在你的控制器中是这样的:

Public class AccountController: Controller
{
    private readonly IOptions<List<IDP>> _IDPs;
    public AccountController(IOptions<List<Defined>> IDPs)
    {
        _IDPs = IDPs;
    }
  ...
}

作为示例,我在上面的控制器中的其他地方使用了它,如下所示:

       _IDPs.Value.ForEach(x => {
            // do something with x
        });

边缘案例

如果您需要多个配置但它们不能在一个数组中并且您不知道在任何时候您将拥有多少 sub-settings。使用以下方法。

appsettings.json

"IDP": {
    "0": {
      "Description": "idp01_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idp01/v1.0",
      "IDPClient": "someapi",
      "Format": "IDP"
    },
    "1": {
      "Description": "idpb2c_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idpb2c",
      "IDPClient": "api1",
      "Format": "IDP"
    },
    "2": {
      "Description": "MyApp",
      "Instance": "https://sts.windows.net/",
      "ClientId": "https://somedomain.com/12345678-5191-1111-bcdf-782d958de2b3",
      "Domain": "somedomain.com",
      "TenantId": "87654321-a10f-499f-9b5f-6de6ef439787",
      "Format": "AzureAD"
    }
  }

型号

public class IDP
{
    public String Description { get; set; }
    public String IDPServer { get; set; }
    public String IDPClient { get; set; }
    public String Format { get; set; }
    public String Instance { get; set; }
    public String ClientId { get; set; }
    public String Domain { get; set; }
    public String TenantId { get; set; }
}

为 Expando 对象创建扩展

public static class ExpandObjectExtension
    {
        public static TObject ToObject<TObject>(this IDictionary<string, object> someSource, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
               where TObject : class, new()
        {
            Contract.Requires(someSource != null);
            TObject targetObject = new TObject();
            Type targetObjectType = typeof(TObject);

            // Go through all bound target object type properties...
            foreach (PropertyInfo property in
                        targetObjectType.GetProperties(bindingFlags))
            {
                // ...and check that both the target type property name and its type matches
                // its counterpart in the ExpandoObject
                if (someSource.ContainsKey(property.Name)
                    && property.PropertyType == someSource[property.Name].GetType())
                {
                    property.SetValue(targetObject, someSource[property.Name]);
                }
            }

            return targetObject;
        }
    }

配置服务

var subSettings = Configuration.GetSection("IDP").Get<List<ExpandoObject>>();

var idx = 0;
foreach (var pair in subSettings)
{

    IDP scheme = ((ExpandoObject)pair).ToObject<IDP>();
    if (scheme.Format == "AzureAD")
    {
        // this is why I couldn't use an array, AddProtecedWebApi requires a path to a config section
        var section = $"IDP:{idx.ToString()}";
        services.AddProtectedWebApi(Configuration, section, scheme.Description);
        // ... do more stuff
        
    }
    idx++;
}
var settingsSection = config.GetSection["someSettings:subSettings"];
var subSettings = new List<string>;

foreach (var section in settingsSection.GetChildren())
{
    subSettings.Add(section.Value);
}

这应该会为您提供所需的值,存储在 subSettings

抱歉提出一个半旧的线程。我很难找到答案,因为大量方法已被弃用,例如 GetGetValue。如果您只需要一个没有配置活页夹的简单解决方案,这应该没问题。 :)

以我为例配置

 services.Configure<List<ApiKey>>(Configuration.GetSection("ApiKeysList"));

未加载,因为属性是 read-only 并且没有默认构造函数

//不工作

  public class ApiKey : IApiKey
    {
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get;  }
        public string OwnerName { get;}
    } 

//工作

    public class ApiKey : IApiKey
    {
        public ApiKey(){}//Added default constructor
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get; set; }        //Added set property
        public string OwnerName { get; set; }  //Added set property
    } 

只需获取整个部分即可填充列表 属性;在设置 class.

services.Configure<Settings>(configuration.GetSection("Another:Some:Example"));

但是...请记住,如果在该列表的设置 class 中设置了默认值...配置设置将是附加的,因此不会覆盖原始值。

所以这些默认值将保留,因此实际上“您无法通过任何其他配置删除它们”

 public List<string> NonEditableStuff { get; set; } = new() { "XYZ1", "LTOY3" };

此外,如果您还打开了 Ini 文件提供程序,知道在此处指定列表时,键并不重要,只要它们是唯一的,因此保留键是有意义的并且列表中的值相同。

[Another:Some:Example:NonEditableStuff]
value=value
whatever2=whatever2