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

Posted

技术标签:

【中文标题】如何从 .net 核心中的 appsettings.json 中提取列表【英文标题】:How to extract a list from appsettings.json in .net core 【发布时间】:2016-08-26 15:24:36 【问题描述】:

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


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

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

config["someSettings:subSettings:0"]

有没有更好的方法将someSettings:subSettings 的内容作为列表检索?

【问题讨论】:

也许这行得通.. weblog.west-wind.com/posts/2016/may/23/… 也许吧。我正在使用不是 asp.net 的控制台应用程序,但我会看看我是否可以获取服务集合。 是的,这也适用于控制台应用程序。这与 asp.net 无关 我问只是因为我得到以下信息:Could not load file or assembly 'Microsoft.Extensions.Configuration.Binder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified 你也可以使用 DTO 类来解析配置 【参考方案1】:

假设您的 appsettings.json 如下所示:


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

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

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

【讨论】:

这对我有用,但我必须先安装“Microsoft.Extensions.Configuration.Binder”NuGet 包,如 here 所述。 要获得键值对象对,您可以使用 json2csharp 创建 C# 类,然后使用 Configuration.GetSection( "foo" ).Get>() 这应该是答案。感谢@Glorfindel 的额外提示! 这对我有用。【参考方案2】:

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

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

    [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

【讨论】:

自我注意:取决于 Microsoft.Extensions.Configuration.Binder 任何提示如何使用services.Configure&lt;TOptions&gt; 做到这一点?我想从包含数组的配置字段中注入选项【参考方案3】:

在 .NetCore 中我就是这样做的:

正常设置:

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

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

创建一个类来为对象建模:

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

注意:如果您需要立即在 ConfigureServices 方法中访问您的列表 你可以使用...

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

边缘案例

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

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

【讨论】:

我创建了一个类来绑定到public class Definitions : List&lt;Defined&gt; 。 ` "定义": [ "名称": "somename", "Title": "sometitle", "Image": "some image url" , "Name": "somename", "Title": "sometitle ", "图片": "一些图片网址" ] `【参考方案4】:
var settingsSection = config.GetSection["someSettings:subSettings"];
var subSettings = new List<string>;

foreach (var section in settingsSection.GetChildren())

    subSettings.Add(section.Value);

这应该给你你需要的值,存储在subSettings

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

【讨论】:

一个简单的答案,但对我来说有一些变化:var settingsSection = config.GetSection("someSettings:subSettings");。换句话说,[] 替换为 () 这个实际上最适合我的用例。【参考方案5】:

在我的情况下配置

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

没有加载,因为属性是只读的并且没有默认构造函数

//不工作

  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
     

【讨论】:

【参考方案6】:

获取整个部分将填充 List 属性;在设置类中。

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

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

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

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

此外,如果您还打开了 Ini 文件提供程序,可能会很方便地知道要在此处指定列表,只要它们是唯一的,键并不重要,因此保留键和相同的值最终出现在列表中。

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

【讨论】:

以上是关于如何从 .net 核心中的 appsettings.json 中提取列表的主要内容,如果未能解决你的问题,请参考以下文章

为asp.net核心中的开发和发布环境自动设置appsettings.json?

从appsettings.json向.net核心控制台应用程序添加多个终结点路由

基于 .Net Core 中的 appSettings 使用 Cors

从 .NET Core 2 中的类中读取 appsettings.json

ConfigurationManager 是不是与 ASP.NET 核心的 appsettings.json 一起使用?

json Asp.net核心访问appsettings.json