如何将POCO类传递给.NET Core配置

the*_*ric 10 .net-core asp.net-core

我正在进行.NET Core应用程序的集成测试,并希望使用一些测试配置.我的配置是一个POCO类,通过appsettings.json然后通过消耗配置IOptions<T>.在我的测试中,我想使用该类的实例.

这是代码:

 var mySettings = GetTestSettings(); // factory method returning POCO class 
 var configurationBuilder = new ConfigurationBuilder();

 // I am looking for something like 
 configurationBuilder.AddInMemoryObject("Settings", mySettings);

 // does not work, result is just string 
 configurationBuilder.AddInMemoryCollection("Settings",
        JsonConvert.SerializeObject(mySettings)); 

// requires filename
configurationBuilder.AddJsonFile("filename.json");
Run Code Online (Sandbox Code Playgroud)

将POCO提供给配置的最简单方法是什么?

Cod*_*ler 9

AddInMemoryCollection获取KeyValuePair键设置键的位置的集合,值是其值.这个来自问题的电话

configurationBuilder.AddInMemoryCollection("Settings", JsonConvert.SerializeObject(mySettings));
Run Code Online (Sandbox Code Playgroud)

实际上将"设置"作为键和整个JSON作为一个设置值传递,这预期不起作用.

但总体方法是正确的,你应该使用AddInMemoryCollection扩展调用.在传递给此调用的集合中,设置键是配置中的完整路径,由冒号分隔.假设您有以下设置POCO:

public class SomeSettings
{
    public string SomeKey1 { get; set; }
    public int SomeKey2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

它是从以下JSON加载的

{
    "SomeSettings": {
        "SomeKey1": "SomeData",
        "SomeKey2": 123
    } 
}
Run Code Online (Sandbox Code Playgroud)

钥匙将是SomeSettings:SomeKey1SomeSettings:SomeKey2.

然后,您可以通过以下AddInMemoryCollection调用添加此类配置:

configurationBuilder.AddInMemoryCollection(new Dictionary<string, string>
{
    { "SomeSettings:SomeKey1", "SomeData" },
    { "SomeSettings:SomeKey2", 123 },
});
Run Code Online (Sandbox Code Playgroud)

现在,如果要通过一次调用添加设置POCO,则可以编写简单的扩展方法,该方法将使用反射枚举设置类属性,并返回属性名称和值的键 - 值对的集合.

这是一个示例:

public static class ObjectExtensions
{
    public static IEnumerable<KeyValuePair<string, string>> ToKeyValuePairs(this Object settings, string settingsRoot)
    {
        foreach (var property in settings.GetType().GetProperties())
        {
            yield return new KeyValuePair<string, string>($"{settingsRoot}:{property.Name}", property.GetValue(settings).ToString());
        }
    }
}

public static class ConfigurationBuilderExtensions
{
    public static void AddInMemoryObject(this ConfigurationBuilder configurationBuilder, object settings, string settingsRoot)
    {
        configurationBuilder.AddInMemoryCollection(settings.ToKeyValuePairs(settingsRoot));
    }
}
Run Code Online (Sandbox Code Playgroud)

在测试中:

var configurationBuilder = new ConfigurationBuilder();
var mySettings = GetTestSettings();
configurationBuilder.AddInMemoryObject(mySettings, "Settings");
Run Code Online (Sandbox Code Playgroud)

这种简单的ObjectExtensions.ToKeyValuePairs扩展方法适用于SomeSettings上面的普通POCO .但是,如果某些属性也是这样的对象,它将无法工作:

public class InnerSettings
{
    public string SomeKey { get; set; }
}

public class OuterSettings
{
    public InnerSettings InnerSettings { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

但是我相信你有一个想法,如果需要可以进行必要的调整.


the*_*ric 6

我最终得到以下结果:

configurationBuilder.AddInMemoryCollection(ToDictionary(GetTestSettings()));

private static IEnumerable<KeyValuePair<string, string>> ToDictionary(object o) => 
    AddPropsToDic(JObject.FromObject(o), new Dictionary<string, string>(), "Settings");

private static Dictionary<string, string> AddPropsToDic(JObject jo, Dictionary<string, string> dic, string prefix)
{
    jo.Properties()
        .Aggregate(dic, (d, jt) =>
        {
            var value = jt.Value;
            var key = $"{prefix}:{jt.Name}";

            if (value.HasValues)
                return AddPropsToDic((JObject) jt.Value, dic, key);

            dic.Add(key, value.ToString());
            return dic;
        });
    return dic;
}
Run Code Online (Sandbox Code Playgroud)