【问题标题】:Microsoft.Extensions.Configuration binding dictionary with colons in keyMicrosoft.Extensions.Configuration 绑定字典,键中有冒号
【发布时间】:2017-05-06 16:22:15
【问题描述】:

我在使用 Microsoft.Extensions.Configuration 绑定包含带有冒号 : 的键的字典时遇到问题。

我做了一个例子,它有一个字典“GoodErrorMappings”,它的键中不包含任何冒号。这些映射正确。

我创建了另一个字典“BadErrorMappings”,键中有一个冒号。在看到字典键中的第一个冒号后,该字典似乎没有正确映射。

我快速查看了源代码,但看不到一个明显的方法来覆盖冒号作为分隔符。

任何帮助将不胜感激。

多科: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration

程序集版本: “Microsoft.NETCore.App” “1.1.0”

C# 代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace OptionsTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json");

            var config = builder.Build();

            var services = new ServiceCollection().AddOptions();

            services.Configure<ApiConfig>(x => config.GetSection("ApiConfig").Bind(x));

            var apiConfig = services.BuildServiceProvider().GetService<IOptions<ApiConfig>>().Value;

            Debug.WriteLine(string.Format("\r\nGoodErrorMappings: {0}", JsonConvert.SerializeObject(apiConfig.GoodErrorMappings, Formatting.Indented)));
            Debug.WriteLine(string.Format("\r\nBadErrorMappings: {0}", JsonConvert.SerializeObject(apiConfig.BadErrorMappings, Formatting.Indented)));

            Console.ReadLine();
        }
    }

    public class ApiConfig
    {
        public Dictionary<string, ErrorMapping> GoodErrorMappings { get; set; } = new Dictionary<string, ErrorMapping>();
        public Dictionary<string, ErrorMapping> BadErrorMappings { get; set; } = new Dictionary<string, ErrorMapping>();
    }

    public class ErrorMapping
    {
        public int HttpStatusCode { get; set; }
        public int ErrorCode { get; set; }
        public string Description { get; set; }
    }
}

AppSettings.json:

{
  "ApiConfig": {
    "GoodErrorMappings": {
      "/SOMEVALUE/BLAH.123": {
        "httpStatusCode": "500",
        "errorCode": "110012",
        "description": "Invalid error description 1"
      },
      "/SOMEVALUE/BLAH.456": {
        "httpStatusCode": "500",
        "errorCode": "110013",
        "description": "Invalid error description 2"
      }
    },
    "BadErrorMappings": {
      "/SOMEVALUE/BLAH:123": {
        "httpStatusCode": "500",
        "errorCode": "110012",
        "description": "Invalid error description 1"
      },
      "/SOMEVALUE/BLAH:456": {
        "httpStatusCode": "500",
        "errorCode": "110013",
        "description": "Invalid error description 2"
      }
    }
  }
}

输出:

GoodErrorMappings: {
  "/SOMEVALUE/BLAH.123": {
    "HttpStatusCode": 500,
    "ErrorCode": 110012,
    "Description": "Invalid error description 1"
  },
  "/SOMEVALUE/BLAH.456": {
    "HttpStatusCode": 500,
    "ErrorCode": 110013,
    "Description": "Invalid error description 2"
  }
}

BadErrorMappings: {
  "/SOMEVALUE/BLAH": {
    "HttpStatusCode": 0,
    "ErrorCode": 0,
    "Description": null
  }
}

【问题讨论】:

    标签: c# configuration asp.net-core .net-core


    【解决方案1】:

    发生这种情况的原因是冒号在配置绑定中具有特殊含义。

    当在键字符串中提供时,冒号可用于标识集合。我已经在您的示例应用程序的以下代码更改中演示了它。我还将您的 BadErrorMappings 更新为绑定中的一个数组,因为这是冒号分隔符正在做的事情。

    程序.cs

    public class Program
        {
            public static void Main(string[] args)
            {
                var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json");
    
                var config = builder.Build();
    
                var services = new ServiceCollection().AddOptions();
    
                services.Configure<ApiConfig>(x => config.GetSection("ApiConfig").Bind(x));
                services.Configure<Fruit>(x => config.GetSection("Fruit").Bind(x));
    
                var serviceProvider = services.BuildServiceProvider();
                var apiConfig = serviceProvider.GetService<IOptions<ApiConfig>>().Value;
                var fruit = serviceProvider.GetService<IOptions<Fruit>>().Value;
    
                Console.WriteLine(string.Format("\r\nGoodErrorMappings: {0}", JsonConvert.SerializeObject(apiConfig.GoodErrorMappings, Formatting.Indented)));
                Console.WriteLine(string.Format("\r\nBadErrorMappings: {0}", JsonConvert.SerializeObject(apiConfig.BadErrorMappings, Formatting.Indented)));
                Console.WriteLine(string.Format("\r\nFruit: {0}", JsonConvert.SerializeObject(fruit, Formatting.Indented)));
    
                Console.ReadLine();
            }
        }
    
        public class Fruit : List<string>
        {
        }
    
        public class ApiConfig
        {
            public Dictionary<string, ErrorMapping> GoodErrorMappings { get; set; } = new Dictionary<string, ErrorMapping>();
            public Dictionary<string, ErrorMapping[]> BadErrorMappings { get; set; } = new Dictionary<string, ErrorMapping[]>();
        }
    
        public class ErrorMapping
        {
            public int HttpStatusCode { get; set; }
            public int ErrorCode { get; set; }
            public string Description { get; set; }
        }
    

    appsettings.json

    {
      "ApiConfig": {
        "GoodErrorMappings": {
          "/SOMEVALUE/BLAH.123": {
            "httpStatusCode": "500",
            "errorCode": "110012",
            "description": "Invalid error description 1"
          },
          "/SOMEVALUE/BLAH.456": {
            "httpStatusCode": "500",
            "errorCode": "110013",
            "description": "Invalid error description 2"
          }
        },
        "BadErrorMappings": {
          "/SOMEVALUE/BLAH:123": {
            "httpStatusCode": "500",
            "errorCode": "110012",
            "description": "Invalid error description 1"
          },
          "/SOMEVALUE/BLAH:456": {
            "httpStatusCode": "500",
            "errorCode": "110013",
            "description": "Invalid error description 2"
          }
        }
      },
      "Fruit:0": "Apple",
      "Fruit:1": "Orange" 
    }
    

    您可以看到 aspnet 团队也在他们的单元测试中利用了这一点,因此这是预期的行为。

    示例: https://github.com/aspnet/Configuration/blob/dev/test/Config.Binder.Test/ConfigurationCollectionBindingTests.cs#L396-L423

    【讨论】:

    【解决方案2】:

    我找到的最简单的解决方案是将您的Dictionary&lt;string, string&gt; 转换为List&lt;KeyValuePair&lt;string, ErrorMapping&gt;&gt;。 JSON.NET 然后将您的 List 转换为具有 { Key: 'keyname', Value: 'value' } 形式的对象数组。如果您接受所需的模型更改,这将很有效。

    使用List&lt;KeyValuePair&lt;string, ErrorMapping&gt;&gt; 而不是Dictionary&lt;string, ErrorMapping&gt;

     public class ApiConfig
        {
            public List<KeyValuePair<string, ErrorMapping>> GoodErrorMappings { get; set; } = new List<KeyValuePair<string, ErrorMapping>>();
            public List<KeyValuePair<string, ErrorMapping>> BadErrorMappings { get; set; } = new List<KeyValuePair<string, ErrorMapping>>();
        }
    

    【讨论】:

    • 我希望不必走这条路,因为之后我需要将列表转换为字典。这可能是我能做到的唯一方法:(
    猜你喜欢
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2012-09-26
    • 2021-11-19
    • 1970-01-01
    • 2015-10-17
    相关资源
    最近更新 更多