出于编程上的便利,我们通常不会直接利用ConfigurationBuilder创建的Configuration对象读取某个单一配置项的值,而是倾向于将一组相关的配置绑定为一个对象,我们将后者称为Options对象。我们在本章第一节通过简单的实例演示了如何利用Options模型实现了配置数据向Options对象的绑定,现在我们对Options模型背后的实现原理进行详细介绍。
目录
一、ConfigurationBinder
二、绑定简单对象
三、绑定复杂对象
四、绑定集合对象
五、绑定字典对象
一、ConfigurationBinder
配置在逻辑上体现为一个具有层次化的配置树,对于一个Options对象来说,如果我们将其数据成员视为其子节点,那么Options对象同样具有一个层次化属性结构,所以Options对象和配置在数据结构层面并没有本质的差异。如果Options类型的数据成员定义与配置的结构具有一一匹配关系,那么将后者绑定为一个对应类型的Options对象是一件很容易的事情,我们本节重点介绍的ConfigurationBinder就是利用这样的原理实现了结构化配置向数据对象的自动绑定。
ConfigurationBinder是一个定义在程序集“Microsoft.Extensions.Configuration.Binder” 中的静态类型,程序集的名称同样也是所在NuGet包的名称,它提供的针对配置的绑定功能体现在它所定义的Bind和一系列Get方法中。如下面的代码片段所示,这些都是针对IConfiguration接口的扩展方法。
1: public static class ConfigurationBinder
2: {
3: public static void Bind(this IConfiguration configuration, object instance);
4:
5: public static object Get(this IConfiguration configuration, Type type);
6: public static object Get(this IConfiguration configuration, Type type, string key);
7: public static T Get<T>(this IConfiguration configuration);
8: public static T Get<T>(this IConfiguration configuration, T defaultValue);
9: public static T Get<T>(this IConfiguration configuration, string key);
10: public static T Get<T>(this IConfiguration configuration, string key, T defaultValue);
11: }
我们可以调用Bind方法将一个Configuration对象绑定为一个预先创建的对象,而Get方法则直接根据指定类型(通过参数type或者方法的泛型参数类型决定)的对应数据对象并将Configuration对象承载的配置数据绑定在该对象上。如果调用具有参数key的Get方法,绑定的配置来源于由这个Key代表的子配置节。
ConfigurationBinder绑定的目标类型可以是一个简单的基元类型,也可以是一个复杂的自定义数据类型,还可以是一个集合或者字典类型。通过上面的介绍我们知道配置的物理结构体现为一个二维数据字典,那么对于绑定生成的不同类型的数据对象,这些原始的数据如何通过一组字符串类型的键值对来表现呢?
二、绑定简单数据类型
由于一个原子配置项总是体现为一个KeyValuePair <string,string >对象,所以配置绑定的原始数据类型是字符串。这里所谓的简单数据类型和复杂数据类型只有一个界定标准,那就是是否支持源自字符串类型的数据转换。简单类型对象可以直接通过一个字符串转换而来,复杂类型对象则不能。
如果绑定的目标类型为简单类型,在进行配置绑定的时候自需要将配置项的值(体现为ConfigurationSection的Value属性)转换成对应的数据类型就可以了。由于所有基元类型(比如Int32、Double等)都是简单类型,所以我们可以直接按照如下的方式绑定它们的值。
1: IConfiguration configuration = new ConfigurationBuilder()
2: .Add(new MemoryConfigurationProvider(new Dictionary<string, string>
3: {
4: ["foo"] = "abc",
5: ["bar"] = "123",
6: ["baz"] = "3.14"
7: })).Build();
8:
9: Debug.Assert(configuration.GetSection("foo").Get<string>() == "abc");
10: Debug.Assert(configuration.Get<int>("bar") == 123);
11: Debug.Assert(configuration.Get<double>("bar") == 3.14);
我们自定义的数据类型在默认情况下不属于简单类型,但是我们可以为它指定一个TypeConverter,如果后者支持源自字符串的类型转换,那么该类型自然也就成为了一个简单类型。如下面的代码片段所示,我们定义了一个表示二维坐标点的Point类型,并通过标注TypeConverterAttribute特性应用了一个之处源自字符串类型转换的TypeConverter(PointTypeConverter)。在进行配置绑定的时候,如果原始配置项具有匹配的格式,则可以直接将其绑定为一个Point对象。
1: [TypeConverter(typeof(PointTypeConverter))]
2: public class Point
3: {
4: public double X { get; set; }
5: public double Y { get; set; }
6: }
7:
8: public class PointTypeConverter : TypeConverter
9: {
10: public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
11: {
12: string[] split = value.ToString().Split(',');
13: return new Point
14: {
15: X = double.Parse(split[0].TrimStart('(')),
16: Y = double.Parse(split[1].TrimStart(')'))
17: };
18: }
19: }
20:
21: IConfiguration configuration = new ConfigurationBuilder().Add(new MemoryConfigurationProvider(new Dictionary<string, string>
22: {
23: ["point"] = "(1.2,3.4)"
24: })).Build();
25: Debug.Assert(configuration.Get<Point>("point").X == 1.2);
26: Debug.Assert(configuration.Get<Point>("point").Y == 3.4);
三、绑定复杂数据类型
如果通过一颗树来表示一个复杂对象,那么真正的数据是通过叶子节点来承载的,并且叶子节点对应的数据类型均为简单类型。如果通过一个二维数据字典来提供一个复杂对象所有的原始数据,那么这个字典中只需要包含叶子节点对应的值即可。至于如何通过一个字典对象体现复杂对象的结构,我们只需要将叶子节点所在的路径作为字典元素的Key就可以了。
1: public class Profile
2: {
3: public Gender Gender { get; set; }
4: public int Age { get; set; }
5: public ContactInfo ContactInfo { get; set; }
6: }
7:
8: public class ContactInfo
9: {
10: public string Email { get; set; }
11: public string PhoneNo { get; set; }
12: }
13:
14: public enum Gender
15: {
16: Male,
17: Female
18: }
如上面的代码片段所示,我们定义了一个表示个人基本信息的Profile类,定义其中的三个属性(Gender、Age和ContactInfo)分别表示性别、年龄和联系方式。表示联系信息的ContactInfo对象具有两个属性(Email和PhoneNo)分别表示电子邮箱地址和电话号码。一个完整的Profile对象可以通过如右图所示的树来体现。
如果需要通过配置的形式来表示一个完整的Profile对象,我们只需要将四个叶子节点(性别、年龄、电子邮箱地址和电话号码)对应的数据定义在配置之中即可。对于承载配置数据的数据字典中,我们需要按照如左边表格所示的方式将这四个叶子节点的路径作为字典元素的Key。
我们通过一个简单示例来演示针对复杂对象的配置绑定。我们创建一个ASP .NET Core控制台应用中,并在project.json文件中添加针对NuGet包“Microsoft.Extensions.Configuration.Binder”的依赖。我们在作为程序入口的Main方法中定义了如下所示的程序。
1: public class Program
2: {
3: public static void Main(string[] args)
4: {
5: IConfiguration configuration = new ConfigurationBuilder().Add(new MemoryConfigurationProvider(new Dictionary<string, string>
6: {
7: ["Profile:Gender"] = "Male",
8: ["Profile:Age"] = "18",
9: ["Profile:ContactInfo:Email"] = "foobar@outlook.com",
10: ["Profile:ContactInfo:PhoneNo"]= "123456789",
11: })).Build();
12:
13: Profile profile = configuration.Get<Profile>("Profile");
14: Console.WriteLine("{0,-10}:{1}", "Gender", profile.Gender);
15: Console.WriteLine("{0,-10}:{1}", "Age", profile.Age);
16: Console.WriteLine("{0,-10}:{1}", "Email", profile.ContactInfo.Email);
17: Console.WriteLine("{0,-10}:{1}", "PhoneNo", profile.ContactInfo.PhoneNo);
18: }
19: }
如上面的代码片段所示,我们创建了一个ConfigurationBuilder对象并为之添加了一个MemoryConfigurationProvider,后者按照如表2所示的结构提供了原始的配置数据。我们利用这个ConfigurationBuilder对象创建的Configuration对象并调用这个ConfigurationSection的Get方法将Key为“Profile”的配置节绑定为一个Profile对象。为了验证配置绑定是否成功,我们最终将这个绑定的Profile对象的相关信息打印出来。该程序执行之后会在控制台上产生如下所示的输出结果。
1: Gender :Male
2: Age :18
3: Email :foobar@outlook.com
4: PhoneNo :123456789
四、绑定集合对象
配置绑定从本质上讲就是承载相同信息的数据在不同结构之间的转换,说得更加具体一点就是数据从基于数据字典的物理结构转换成基于树的逻辑结构。要理解针对集合的配置绑定,需要首先了解一个集合对象在逻辑上体现怎样一棵树。对于一个集合对象来说,组成它的元素自然视为集合的子节点,所以一个包含三个Profile对象的集合可以由左图所示的树来体现。
但是如8所示的这棵树并不是一个合法的配置树。对于这棵树来说,表示集合元素的配置节都采用“Profile”作为Key,这导致了所有叶子节点的路径并不是唯一的。由于路径不唯一,我们自然不能将它作为一个字典对象的Key,那么构成这个集合的原子数据将无法通过一个数据字典来承载。为了解决这个问题,我们将集合元素的索引(0、1、2、 …)对应的配置节的Key,所以右图所示的才是真正配置树的结构。
既然我们能够正确将集合对象通过一个合法的配置树体现出来,那么我们就可以直接利用一个字典对象来提供构成这个集合对象的所有原子数据。数据字典中的每一个元素对应着配置树中的某个叶子结点,后者的路径直接作为字典元素的Key, 下面的表格清晰地体现了这个数据字典的结构。
我们依然通过一个简单的实例来演示针对集合的配置绑定。如下面的代码片段所示,我们创建了一个ConfigurationBuilder对象并为之添加了一个MemoryConfigurationProvider,后者按照如表3所示的结构提供了原始的配置数据。我们利用这个ConfigurationBuilder对象创建的Configuration对象并调用这个ConfigurationSection的Get方法将Key为“Profiles”的配置节绑定为一个List<Profile>对象。
1: public class Program
2: {
3: public static void Main(string[] args)
4: {
5: IConfiguration configuration = new ConfigurationBuilder().Add(new MemoryConfigurationProvider(new Dictionary<string, string>
6: {
7: ["Profiles:0:Gender"] = "Male",
8: ["Profiles:0:Age"] = "18",
9: ["Profiles:0:ContactInfo:Email"] = "foo@gmail.com",
10: ["Profiles:0:ContactInfo:PhoneNo"] = "123",
11:
12: ["Profiles:1:Gender"] = "Male",
13: ["Profiles:1:Age"] = "25",
14: ["Profiles:1:ContactInfo:Email"] = "bar@gmail.com",
15: ["Profiles:1:ContactInfo:PhoneNo"] = "456",
16:
17: ["Profiles:2:Gender"] = "Female",
18: ["Profiles:2:Age"] = "40",
19: ["Profiles:2:ContactInfo:Email"] = "baz@gmail.com",
20: ["Profiles:2:ContactInfo:PhoneNo"] = "789",
21: })).Build();
22:
23: IEnumerable<Profile> profiles = configuration.Get<List<Profile>>("Profiles");
24: foreach (Profile profile in profiles)
25: {
26:
27: Console.WriteLine("{0,-10}:{1}", "Gender", profile.Gender);
28: Console.WriteLine("{0,-10}:{1}", "Age", profile.Age);
29: Console.WriteLine("{0,-10}:{1}", "Email", profile.ContactInfo.Email);
30: Console.WriteLine("{0,-10}:{1}\n", "PhoneNo", profile.ContactInfo.PhoneNo);
31: }
32: }
33: }
为了验证配置绑定是否成功,我们最终将这个绑定的List<Profile>对象的每个元素的相关信息打印出来,该程序执行之后会在控制台上产生如下所示的输出结果。
1: Gender :Male
2: Age :18
3: Email :foo@gmail.com
4: PhoneNo :123
5:
6: Gender :Male
7: Age :25
8: Email :bar@gmail.com
9: PhoneNo :456
10:
11: Gender :Female
12: Age :40
13: Email :baz@gmail.com
14: PhoneNo :789
五、绑定字典
字典可以视为元素类型为键值对的集合,两者在配置树上的表示非常相似,它们之间的唯一不同之处在于前者采用索引作为集合元素所在配置节的Key,后者直接将键值对的Key直接作为配置节的Key。举个简单的例子,我们通过一个Dictionary<string, Profile >对象来表示多个用户的个人信息,并且将用户名作为这个字典的Key,那么这个字典对象的配置树将具有如右图所示的结构(“Foo”、“Bar”和“Baz”表示用户名的Key)。
表示集合与字典的配置树在结构上基本类似,所以反映在基于数据字典的物理结构上也大同小异。对于右图表示的Dictionary<string, Profile>对象,构造该对象的所有原子配置数据可以通过包含如下元素的数据字典来提供。
我们依然通过一个简单的实例来演示针对字典的配置绑定。如下面的代码片段所示,我们创建了一个ConfigurationBuilder对象并为之添加了一个MemoryConfigurationProvider,后者按照如左边表格所示的结构提供了原始的配置数据。我们利用这个ConfigurationBuilder对象创建的Configuration对象并调用这个ConfigurationSection的Get方法将Key为“Profiles”的配置节绑定为一个Dictionary<string, Profile>对象。
1: public class Program
2: {
3: public static void Main(string[] args)
4: {
5: IConfiguration configuration = new ConfigurationBuilder().Add(new MemoryConfigurationProvider(new Dictionary<string, string>
6: {
7: ["Profile:Foo:Gender"] = "Male",
8: ["Profile:Foo:Age"] = "18",
9: ["Profile:Foo:ContactInfo:Email"] = "foo@gmail.com",
10: ["Profile:Foo:ContactInfo:PhoneNo"] = "123",
11:
12: ["Profile:Bar:Gender"] = "Male",
13: ["Profile:Bar:Age"] = "25",
14: ["Profile:Bar:ContactInfo:Email"] = "bar@gmail.com",
15: ["Profile:Bar:ContactInfo:PhoneNo"] = "456",
16:
17: ["Profile:Baz:Gender"] = "Female",
18: ["Profile:Baz:Age"] = "40",
19: ["Profile:Baz:ContactInfo:Email"] = "baz@gmail.com",
20: ["Profile:Baz:ContactInfo:PhoneNo"] = "789",
21: })).Build();
22:
23: Dictionary<string, Profile> profiles = configuration.Get<Dictionary<string, Profile>>("Profile");
24: foreach (var item in profiles)
25: {
26:
27: Console.WriteLine("{0,-10}:{1}", "Name", item.Key );
28: Console.WriteLine("{0,-10}:{1}", "Gender", item.Value.Gender);
29: Console.WriteLine("{0,-10}:{1}", "Age", item.Value.Age);
30: Console.WriteLine("{0,-10}:{1}", "Email", item.Value.ContactInfo.Email);
31: Console.WriteLine("{0,-10}:{1}\n", "PhoneNo", item.Value.ContactInfo.PhoneNo);
32: }
33: }
34: }
为了验证配置绑定是否成功,我们最终将这个绑定的Dictionary<string, Profile>对象的每个元素的相关信息打印出来,该程序执行之后会在控制台上产生如下所示的输出结果。
1: Name :Foo
2: Gender :Male
3: Age :18
4: Email :foo@gmail.com
5: PhoneNo :123
6:
7: Name :Bar
8: Gender :Male
9: Age :25
10: Email :bar@gmail.com
11: PhoneNo :456
12:
13: Name :Baz
14: Gender :Female
15: Age :40
16: Email :baz@gmail.com
17: PhoneNo :789
ASP.NET Core的配置(1):读取配置信息
ASP.NET Core的配置(2):配置模型详解
ASP.NET Core的配置(3): 将配置绑定为对象[上篇]
ASP.NET Core的配置(3): 将配置绑定为对象[下篇]
ASP.NET Core的配置(4):多样性的配置源[上篇]
ASP.NET Core的配置(4):多样性的配置源[中篇]
ASP.NET Core的配置(4):多样性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]
ASP.NET Core的配置(3): 将配置绑定为对象[上篇]的更多相关文章
-
asp.net core 教程(五)-配置
Asp.Net Core-配置 Asp.Net Core-配置 在这一章,我们将讨论 ASP.NET Core项目的相关的配置.在解决方案资源管理器中,您将看到 Startup.cs 文件.如果你有以 ...
-
asp.net core重新加载应用配置
asp.net core重新加载应用配置 Intro 我把配置放在了数据库或者是Redis里,配置需要修改的时候我要直接修改数据库,然后调用一个接口去重新加载应用配置,于是就尝试写一个运行时重新加载配 ...
-
Linux CentOS7部署ASP.NET Core应用程序,并配置Nginx反向代理服务器
前言: 本篇文章主要讲解的是如何在Linux CentOS7操作系统搭建.NET Core运行环境并发布ASP.NET Core应用程序,以及配置Nginx反向代理服务器.因为公司的项目一直都是托管在 ...
-
ASP.NET Core应用程序的参数配置及使用(转载)
本文结构 提前准备 参数配置方式 appsettings.json 环境变量 命令行参数 在控制器中使用配置参数 注入IConfiguration对象 注入IOptions对象 总结 应用程序的开发不 ...
-
ASP.NET Core框架深度学习(二) 管道对象
4.HttpContext 第一个对象 我们的ASP.NET Core Mini由7个核心对象构建而成.第一个就是大家非常熟悉的HttpContext对象,它可以说是ASP.NET Core应用开发中 ...
-
ASP.NET Core 在 JSON 文件中配置依赖注入
前言 在上一篇文章中写了如何在MVC中配置全局路由前缀,今天给大家介绍一下如何在在 json 文件中配置依赖注入. 在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等 ...
-
ASP.NET Core 注入和获取 AppSettings 配置
ASP.NET Core 项目中有个appsettings.json配置文件,用于存放一些配置信息,比如数据库连接字符串等,但访问的话,只能在 ASP.NET Core 项目中获取,如果我们在其他项目 ...
-
asp.net core 2.0中的配置(1)---Configuration
配置就是一个装配数据字典的过程,一个字典也就是一个键值对,所以从配置就是键值对. 在asp.net core中关于配置是由四个基本的类型来支撑的,是①IConfigurationSource②ICon ...
-
ASP.NET Core 1.0 基础之配置
来源https://docs.asp.net/en/latest/fundamentals/configuration.html ASP.NET Core 1.0支持不同的配置选项.应用配置数据可以是 ...
随机推荐
-
访问API安全性认证设计
1.用户POST登录(账号+密码) | |成功 |2.返回(Private key+时间戳)加密字符串+用户信息+缓存到内存中 | |发起其它请求 |3.发起请求时携带Request参数和(Reque ...
-
Android 命令管理项目
今天介绍一下Android怎么用命令管理项目,用命令管理Android项目需要用到Android命令,首先介绍一下用Android命令创建新项目,打开命令提示窗口,导航到Android SDK 的to ...
-
Orchard Platform v1.7.2 发布
发布说明: 1. 添加Json格式数据文件支持.2. 删除了Settings, Modules, Themes模块中的Routers和Controllers.3. 删除了默认的ContentType, ...
-
HDU2112 HDU Today 最短路+字符串哈希
HDU Today Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
-
Nexus远程Maven仓库索引下载教程
下载Maven仓库索引有两种方式: 一.手动下载 首先将索引下载到本地,下载地址:nexus-maven-repository-index.zip 解压索引压缩包,将里面内容全部拷贝 关闭当前Ne ...
-
BZOJ 2115 Wc2011 Xor DFS+高斯消元
标题效果:鉴于无向图.右侧的每个边缘,求一个1至n路径,右上路径值XOR和最大 首先,一个XOR并能为一个路径1至n简单的路径和一些简单的XOR和环 我们开始DFS获得随机的1至n简单的路径和绘图环所 ...
-
Jquery 多选全选/取消 选项卡切换 获取选中的值
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
-
WebService-axis2
WebService框架有好多,常用的cxf,axis2等,axis2的配置过程相对简单,不用编写接口,在实现.只需要一个Service服务类即可.配置过程大致如下: 1,导入jar包(这里我是把ax ...
-
JavaSE之绘制菱形
在JavaSE的算法练习中,绘制菱形是一个比较常见的案例.菱形效果如下图所示: 我们在解决算法问题时,通常情况下,先不要急于马上编码,而是要先观察,找出解决问题的关键所在. 在上图中,我们可以看到,菱 ...
-
springboot:快速构建一个springboot项目
前言: springboot作为springcloud的基础,springboot的热度一直很高,所以就有了这个springboot系列,花些时间来了解和学习为自己做技术储备,以备不时之需[手动滑稽] ...