简体   繁体   English

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

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

I have an appsettings.json file which looks like this:我有一个 appsettings.json 文件,它看起来像这样:

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

When I build my configuration root, and do something like config["someSetting:subSettings"] it returns null and the actual settings available are something like this:当我构建我的配置根目录并执行类似config["someSetting:subSettings"]它返回 null 并且可用的实际设置如下所示:

config["someSettings:subSettings:0"]

Is there a better way of retrieving the contents of someSettings:subSettings as a list?有没有更好的方法来检索someSettings:subSettings作为列表的内容?

Assuming your appsettings.json looks like this:假设您的appsettings.json如下所示:

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

You can extract the list items like so:您可以像这样提取列表项:

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

You can use the Configuration binder to get a strong type representation of the configuration sources.您可以使用配置绑定器来获取配置源的强类型表示。

This is an example from a test that I wrote before, hope it helps:这是我之前写的一个测试的例子,希望它有帮助:

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

The important part is the call to Bind .重要的部分是对Bind的调用。

The test and more examples are on GitHub测试和更多示例在GitHub 上

In .NetCore this is what I did:在 .NetCore 这就是我所做的:

Normal Setup:正常设置:

In your appsettings.json create a configuration section for your custom definitions:在您的 appsettings.json 中为您的自定义定义创建一个配置部分:

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

Create a class to model the objects:创建一个类来为对象建模:

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

}

in your Startup -> ConfigureServices在您的 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...注意:如果您需要在 ConfigureServices 方法中立即访问您的列表,您可以使用...

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

Then in your controller something like this:然后在你的控制器中是这样的:

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

just as an example I used it elsewhere in the above controller like this:作为一个例子,我在上面的控制器中的其他地方使用了它,如下所示:

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

Edge Case边缘情况

In the case that you need multiple configs but they can't be in an array and you have no idea how many sub-settings you will have at any one time.如果您需要多个配置但它们不能在一个数组中,并且您不知道在任何时候您将拥有多少个子设置。 Use the following method.使用以下方法。

appsettings.json 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"
    }
  }

Model模型

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

Create Extension for Expando Object为 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;
        }
    }

ConfigureServices配置服务

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

This should give you the values you need, stored in subSettings这应该为您提供所需的值,存储在subSettings

Apologies for bringing up a semi-old thread.为提出半旧线程而道歉。 I had difficulty finding an answer as a good amount of methods are deprecated, like Get and GetValue .我很难找到答案,因为不推荐使用大量方法,例如GetGetValue This should be fine if you only need a simple solution without the configuration binder.如果您只需要一个没有配置绑定器的简单解决方案,这应该没问题。 :) :)

In my case configuration在我的情况下配置

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

wasn't loaded because the properties were read-only and there were no default constructor未加载,因为属性是只读的并且没有默认构造函数

//Not working //不工作

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

//Working //在职的

    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
    } 

Just getting the whole section will populate the List property;只需获取整个部分将填充 List 属性; in a settings class.在设置类中。

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

But... do remember that if defaults are set in the settings class for that List... that the configuration settings will be additive and so not overwriting the original values.但是...请记住,如果在该列表的设置类中设置了默认值...配置设置将是附加的,因此不会覆盖原始值。

So these defaults will remain and so are really "no way you can delete them via any additional configuration"因此,这些默认值将保留,因此“您无法通过任何其他配置删除它们”

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

Also, if you also have turned on the Ini file provider might be be handy to know that to specify the list there the keys do not really matter as long as they are unique, so it makes sense to keep the key and the values there the same to end up in the list.此外,如果您还打开了 Ini 文件提供程序可能会很方便地知道在那里指定列表,只要它们是唯一的,键就无关紧要,因此将键和值保留在那里是有意义的同样会出现在列表中。

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

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

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