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

我有一个 appsetings.json 文件,它看起来像这样:

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

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

config["someSettings:subSettings:0"]

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

95480 次浏览

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

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

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

测试和更多的例子都在 GitHub

假设你的 appsettings.json是这样的:

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

您可以这样提取列表项:

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

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

正常设置:

在 appsetings.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; }


}

in your Startup -> ConfigureServices

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

注意: 如果需要立即访问 ConfigureServices 方法中的列表 you can use...

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

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

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

Create Extension for Expando Object

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

没有加载,因为属性是只读的,没有缺省构造函数

不起作用

  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
}

只需获取整个部分即可填充 List 属性; 在设置类中。

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

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

所以这些默认值仍然存在,所以“你不可能通过任何额外的配置删除它们”

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

另外,如果您也打开了 Ini 文件提供程序,那么您可能会很方便地知道,只要键是唯一的,指定那里的列表并不重要,因此保持键和那里的值相同以在列表中结束是有意义的。

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