ASP.Net JSON配置文件使用数组进行转换

Mat*_*att 6 c# asp.net appsettings .net-core asp.net-core

我有一个基本配置文件,例如.

appsettings.json

{
    "Values": {
        "Test": ["one", "two"]
    }
}
Run Code Online (Sandbox Code Playgroud)

appsettings.dev.json

{
    "Values": {
        "Test": ["three"]
    }
}
Run Code Online (Sandbox Code Playgroud)

在转换之后,数组将是

["three", "two"]
Run Code Online (Sandbox Code Playgroud)

如何确保转换后的数组缩小到较少数量的元素,而不是每个元素单独更改?

Cod*_*ler 7

要了解被覆盖的数组设置的这种"奇怪"行为的原因,您需要了解这些设置如何存储在配置提供程序中.

实际情况是,所有加载的设置都存储在字典中,为每个配置提供程序拥有.键是通过设置路径构建的,其中嵌套部分用冒号分隔.阵列的设置存储在相同的词典,在设定路径(一个索引:0,:1...).

对于您描述的配置,您将拥有2个配置提供程序,其中包含以下设置:

provider1[Values:Test:0] = "one"
provider1[Values:Test:1] = "two"
Run Code Online (Sandbox Code Playgroud)

provider2[Values:Test:0] = "three"
Run Code Online (Sandbox Code Playgroud)

配置提供程序中的值

现在很清楚为什么数组设置的最终值是["three", "two"].Values:Test:0从第二个提供程序覆盖第一个提供程序的相同设置,并Values:Test:1保持不变.

不幸的是,现在有一种内在的可能性来克服这个问题.幸运的是,.net核心配置模型足够灵活,可根据您的需要调整此行为.

想法如下:

  1. 以相反顺序枚举配置提供程序.
  2. 为每个提供商获取其所有设置键.您可以IConfigurationProvider.GetChildKeys()为此目的递归调用方法.请参阅GetProviderKeys()下面的代码段.
  3. 使用正则表达式检查当前键是否是数组条目.
  4. 如果是,并且某些先前的提供程序会覆盖此数组,则只需通过将其设置为nullvalue 来抑制当前数组条目.
  5. 如果它是看不见的数组,则当前提供程序被标记为此数组的唯一值提供程序.来自所有其他提供者的数组将被抑制(步骤#4).

为方便起见,您可以将所有这些逻辑包装到扩展方法中IConfigurationRoot.

这是一个工作样本:

public static class ConfigurationRootExtensions
{
    private static readonly Regex ArrayKeyRegex = new Regex("^(.+):\\d+$", RegexOptions.Compiled);

    public static IConfigurationRoot FixOverridenArrays(this IConfigurationRoot configurationRoot)
    {
        HashSet<string> knownArrayKeys = new HashSet<string>();

        foreach (IConfigurationProvider provider in configurationRoot.Providers.Reverse())
        {
            HashSet<string> currProviderArrayKeys = new HashSet<string>();

            foreach (var key in GetProviderKeys(provider, null).Reverse())
            {
                //  Is this an array value?
                var match = ArrayKeyRegex.Match(key);
                if (match.Success)
                {
                    var arrayKey = match.Groups[1].Value;
                    //  Some provider overrides this array.
                    //  Suppressing the value.
                    if (knownArrayKeys.Contains(arrayKey))
                    {
                        provider.Set(key, null);
                    }
                    else
                    {
                        currProviderArrayKeys.Add(arrayKey);
                    }
                }
            }

            foreach (var key in currProviderArrayKeys)
            {
                knownArrayKeys.Add(key);
            }
        }

        return configurationRoot;
    }

    private static IEnumerable<string> GetProviderKeys(IConfigurationProvider provider,
        string parentPath)
    {
        var prefix = parentPath == null
                ? string.Empty
                : parentPath + ConfigurationPath.KeyDelimiter;

        List<string> keys = new List<string>();
        var childKeys = provider.GetChildKeys(Enumerable.Empty<string>(), parentPath)
            .Distinct()
            .Select(k => prefix + k).ToList();
        keys.AddRange(childKeys);
        foreach (var key in childKeys)
        {
            keys.AddRange(GetProviderKeys(provider, key));
        }

        return keys;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一件事是在构建配置时调用它:

IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("AppSettings.json")
    .AddJsonFile("appsettings.dev.json");
var configuration = configurationBuilder.Build();
configuration.FixOverridenArrays();
Run Code Online (Sandbox Code Playgroud)