ASP.NET Core的配置(3): 将配置绑定为对象[上篇]

出于编程上的便利,我们通常不会直接利用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"]  = "[email protected]",

  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     :[email protected]

   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"]   = "[email protected]",

  10:                 ["Profiles:0:ContactInfo:PhoneNo"]     = "123",

  11:  

  12:                 ["Profiles:1:Gender"]       = "Male",

  13:                 ["Profiles:1:Age"]      = "25",

  14:                 ["Profiles:1:ContactInfo:Email"]       = "[email protected]",

  15:                 ["Profiles:1:ContactInfo:PhoneNo"]     = "456",

  16:  

  17:                 ["Profiles:2:Gender"]       = "Female",

  18:                 ["Profiles:2:Age"]      = "40",

  19:                 ["Profiles:2:ContactInfo:Email"]       = "[email protected]",

  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     :[email protected]

   4: PhoneNo   :123

   5:  

   6: Gender    :Male

   7: Age       :25

   8: Email     :[email protected]

   9: PhoneNo   :456

  10:  

  11: Gender    :Female

  12: Age       :40

  13: Email     :[email protected]

  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"]    = "[email protected]",

  10:                 ["Profile:Foo:ContactInfo:PhoneNo"]  = "123",

  11:  

  12:                 ["Profile:Bar:Gender"]               = "Male",

  13:                 ["Profile:Bar:Age"]                  = "25",

  14:                 ["Profile:Bar:ContactInfo:Email"]    = "[email protected]",

  15:                 ["Profile:Bar:ContactInfo:PhoneNo"]  = "456",

  16:                     

  17:                 ["Profile:Baz:Gender"]               = "Female",

  18:                 ["Profile:Baz:Age"]                  = "40",

  19:                 ["Profile:Baz:ContactInfo:Email"]    = "[email protected]",

  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     :[email protected]

   5: PhoneNo   :123

   6:  

   7: Name      :Bar

   8: Gender    :Male

   9: Age       :25

  10: Email     :[email protected]

  11: PhoneNo   :456

  12:  

  13: Name      :Baz

  14: Gender    :Female

  15: Age       :40

  16: Email     :[email protected]

  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的配置(5):配置的同步

时间: 2024-12-28 20:13:27

ASP.NET Core的配置(3): 将配置绑定为对象[上篇]的相关文章

asp.net core系列 28 EF模型配置(字段,构造函数,拥有实体类型)

原文:asp.net core系列 28 EF模型配置(字段,构造函数,拥有实体类型) 一. 支持字段 EF允许读取或写入字段而不是一个属性.在使用实体类时,用面向对象的封装来限制或增强应用程序代码对数据访问的语义时,这可能很有用.无法使用数据注释配置.除了约定,还可以使用Fluent API为属性配置支持字段. 1.1 约定 public class Blog { // _<camel-cased property name> private string _url; public int

将配置绑定为对象[上篇]

将配置绑定为对象[上篇] 出于编程上的便利,我们通常不会直接利用ConfigurationBuilder创建的Configuration对象读取某个单一配置项的值,而是倾向于将一组相关的配置绑定为一个对象,我们将后者称为Options对象.我们在本章第一节通过简单的实例演示了如何利用Options模型实现了配置数据向Options对象的绑定,现在我们对Options模型背后的实现原理进行详细介绍. 目录一.ConfigurationBinder二.绑定简单对象三.绑定复杂对象四.绑定集合对象五.

asp.net core在Centos7环境运行配置

环境说明: 服务器系统:CentOS 7.2.1511 相关工具:Xshel.Xftp 服务器软件软件:.netcore.nginx.supervisor 准备好发布的程序 安装.NET Core SDK for CentOS7   打开网址:https://www.microsoft.com/net/core#linuxcentos 复制如下命令,单步执行: sudo yum install libunwind libicu   curl -sSL -o dotnet.tar.gz https

asp.net core系列 27 EF模型配置(索引,备用键,继承)

一.索引 索引是许多数据存储中的常见概念.虽然它们在数据存储中的实现可能会有所不同,但它们可用于更有效地基于列(或列集)进行查找.按照约定,用作外键每个属性 (或组的属性) 会自动创建索引.无法使用数据注释创建索引. 1.1 非唯一索引 Fluent API 在单个属性上指定索引.默认情况下,索引是非唯一的.如下代码示例在Blogs表上创建Url列索引: class MyContext : DbContext { public DbSet<Blog> Blogs { get; set; } p

asp.net core不通过构造方法从容器中获取对象及解决通过这种方法NLog获取对象失败的问题

一般想从容器中获取对象,我们都是通过构造方法获取对象,但有些条件不允许不能通过构造方法获取对象,我们必须单独从容器中单独创建获取找个对象,这样我们就不行把找个容器静态保存起来供全局diaoy 一. 简单些一下步骤如下:(从某一个大神视频或者代码中学习到的,具体哪个不记得了) 1.先做一个构建对象的接口IEngine public interface IEngine { /// <summary> /// 构建一个实例 /// </summary> /// <typeparam

ASP.NET Core实现强类型Configuration读取配置数据

前言 实现读取JSON文件几种方式,在项目中采取老办法简单粗暴,结果老大过来一看,恩,这样不太可取,行吧那我就用.NET Core中最新的方式诺,切记,适合的才是最好的,切勿懒. .NET Core读取JSON文件通过读取文件方式 当我将VS2015项目用VS2017打开后再添加控制器,此时会报错如下: 此时我们应该在该项目中的.csproj中添加如下这一句才能解决此问题: <ItemGroup> <DotNetCliToolReference Include="Microso

ASP.NET CORE读取appsettings.json的配置

如何在appsettings.json配置应用程序设置,微软给出的方法:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration 下面是我的做法: 因为我建立的是空项目什么都没有,好多东西都需要新建和引用,新建appsettings.json文件,然后添加一个AppSettings字段,包含配置和值 在Models文件夹下创建一个AppSettingsModel.cs NuGet包管理器引用或者在project

asp.net core 简单部署之FTP配置(CentOS 7.0安装配置Vsftp服务器)

配置过程原文地址:http://www.osyunwei.com/archives/9006.html 坑和结果 正确的跟着这个内容走,是靠谱的. 我自己给自己踩了个坑,请参照文章的朋友注意第七条:七.在/etc/pam.d/vsftpd的文件头部加入以下信息(在后面加入无效) 一些简单操作和命令,小白的我也是吃尽了苦头: cat:查看文件  cat /etc/vsftpd/virtusers vi:编辑文件  vi /etc/vsftpd/virtusers. 编辑文件需要注意,按键盘I是开始

ASP.NET Core根据环境切换NLog配置

1.新建NLog配置文件,名称分别为nlog.config和nlog.debug.config <?xml version="1.0"?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" inter