加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

.NET Core采用的全新配置系统[4]: “Options模式”下各种类型的O

发布时间:2020-12-16 09:06:52 所属栏目:asp.Net 来源:网络整理
导读:旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上。配置绑定的目标类型可以是一个简单的 基元类型 ,也可以是一个 自定义数据类型 ,还可以是一个 数组 、 集合 或者 字典 类型。通过前面的介绍我们知道ConfigurationProvider将原始

旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上。配置绑定的目标类型可以是一个简单的基元类型,也可以是一个自定义数据类型,还可以是一个数组集合或者字典类型。通过前面的介绍我们知道ConfigurationProvider将原始的配置数据读取出来后会将其转成Key和Value均为字符串的数据字典,那么针对这些完全不同的目标类型,原始的配置数据如何通过数据字典的形式来体现呢? [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、绑定简单数据类型
二、绑定复杂数据类型
三、绑定集合对象
四、绑定字典

一、绑定简单数据类型

我们先来说说针对简单数据类型的配置绑定。这里所谓的简单数据类型和复杂数据类型只有一个界定标准,那就是是否支持源自字符串类型的数据转换。也就是说,简单类型对象可以直接通过一个字符串转换而来,复杂类型对象则不能。如果目标类型是一个简单类型,在进行配置绑定的时候只需要将配置项的值(体现为ConfigurationSection的Value属性)转换成对应的数据类型就可以了。

对于简单类型的配置绑定,除了调用上述的扩展方法Bind来完成之外,我们其实还有更好的选择,那就是调用IConfiguration接口的另一个扩展方法GetValue。GetValue方法总是将一个原子配置项的值(字符串)转换成目标类型,所以我们在调用该方法是除了指定目标类型之外,还需要通过参数key指定这个原子配置项相对于当前Configuration对象的路径,也就是说参数key不仅仅可以指定为子配置项的Key(比如“Foo”),也可以设定为以下每个配置节相对于当前节点的路径(比如“Foo:Bar:Baz”)。如果指定的配置节没有值,或者配置节根本不存在,该方法会返回通过defaultValue参数指定的默认值。

   1: public static object GetValue(this IConfiguration configuration,Type type,string key,1)">object defaultValue) ;

除了上述这个GetValue方法之外,IConfiguration接口还具有如下三个GetValue方法重载,它们最终都会调用上面这个方法来完成针对简单类型的配置绑定。前面两个方法以泛型参数的形式指定绑定的目标类型,如果没有显式指定默认值,意味着默认值为Null。

   2: 
2: {
   3:     ["foo"] = "3.14159265",
   4:     ["bar"] = "Female",1)'>   5:     ["baz"] = "(1.1,2.2)"
   6: };
   7:? 
   8: IConfiguration config = new ConfigurationBuilder()
   9:     .Add(new MemoryConfigurationSource { InitialData = source })
  10:     .Build();
  11:? 
  12: Debug.Assert(config.GetValue<double>("foo") == 3.14158265);
  13: Debug.Assert(config.GetValue<Gender>("bar") == Gender.Female);
  14: Debug.Assert(config.GetValue<Point>("baz").X == 1.1);
  15: Debug.Assert(config.GetValue<Point>("baz").Y == 2.2);
  16:? 
  17: enum Gender
  18: {
  19:     Male,1)'>  20:     Female
  21: }
  22:? 
  23: [TypeConverter(typeof(PointTypeConverter))]
  24: class Point
  25: {
  26:     double X { get; set; }
  27:     double Y { get; set; }
  28:        
  29:? 
  30: }
  31: class PointTypeConverter : TypeConverter
  32: {
  33:     override object ConvertFrom(ITypeDescriptorContext context,CultureInfo culture,1)">object value)
  34:     {
  35:         string[] split = value.ToString().Split(',');
  36:         double x = double.Parse(split[0].Trim().TrimStart('('));
  37:         double y = double.Parse(split[1].Trim().TrimEnd(')'));
  38:         return new Point { X = x,Y = y };
  39:     }
  40: }

二、绑定复杂数据类型

这里所谓的复杂类型表示一个具有属性数据成员的类型。如果通过一颗树来表示一个复杂对象,那么叶子节点承载所有的数据,并且叶子节点的数据类型均为简单类型。如果通过数据字典来提供一个复杂对象所有的原始数据,那么这个字典中只需要包含叶子节点对应的值即可。至于如何通过一个字典对象体现复杂对象的结构,我们只需要将叶子节点所在的路径作为字典元素的Key就可以了。

3: public Gender Gender { get; set; }
   5:     public ContactInfo    ContactInfo { get; set; }
   8: class ContactInfo
  10:     string EmailAddress { get; set; }
  12: }
  14:   15: {
  18: }

如上面的代码片段所示,我们定义了一个表示个人基本信息的Profile类,定义其中的三个属性(Gender、Age和ContactInfo)分别表示性别、年龄和联系方式。表示联系信息的ContactInfo对象具有两个属性(EmailAddress和PhoneNo)分别表示电子邮箱地址和电话号码。一个完整的Profile对象可以通过如下图所示的树来体现。

如果需要通过配置的形式来表示一个完整的Profile对象,我们只需要将四个叶子节点(性别、年龄、电子邮箱地址和电话号码)对应的数据定义在配置之中即可。对于承载配置数据的数据字典中,我们需要按照如下表所示的方式将这四个叶子节点的路径作为字典元素的Key。

Key

Value

Gender

Male

Age

18

ContactInfo:Email

foobar@outlook.com

ContactInfo:PhoneNo

123456789

如上面的代码片段所示,我们创建了一个ConfigurationBuilder对象并为之添加了一个MemoryConfigurationProvider,后者按照如表2所示的结构提供了原始的配置数据。我们完全按照Options编程模式将这些原始的配置属性绑定成一个Profile对象。

7: };
   9: IConfiguration config =   10:     .Add(  11:     .Build();
  13: Profile profile = new ServiceCollection()
  15:     .Configure<Profile>(config)
  17:     .GetService<IOptions<Profile>>()
"foo:gender"]                       = "foo:age"]                          = "foo:contactInfo:emailAddress"]     = "foo@outlook.com",1)">"foo:contactInfo:phoneNo"]          = "123",1)">   8:     ["bar:gender"]                       =    9:     ["bar:age"]                          = "25",1)">  10:     ["bar:contactInfo:emailAddress"]     = "bar@outlook.com",1)">  11:     ["bar:contactInfo:phoneNo"]          = "456",1)">  13:     ["baz:gender"]                       =   14:     ["baz:age"]                          = "36",1)">  15:     ["baz:contactInfo:emailAddress"]     = "baz@outlook.com",1)">  16:     ["baz:contactInfo:phoneNo"]          = "789"
  18:? 
  23: Collection<Profile> profiles =   24:     .AddOptions()
  26:     .BuildServiceProvider()
  28:     .Value;

?

针对集合类型的配置绑定,还有一个不为人知的小细节值得一提。IConfiguration接口的Bind方法在进行集合绑定的时候,如果某个元素绑定失败,并不会有任何的异常会被抛出,该方法会选择下一个元素继续实施绑定。这个特性会造成最终生成的集合对象与原始配置在数量上的不一致。比如我们将上面的程序作了如下的改写,保存原始配置的字典对象包含两个元素,第一个元素的性别从“Male”改为“男”,毫无疑问这个值是不可能转换成Gender枚举对象的,所以针对这个Profile的配置绑定会失败。代码整个程序并不会有任何异常抛出来,但是最终生成的Collection<Profile>将只有一个元素。

12: };
  17:? 
  20:     .Configure<Collection<Profile>>(config)
  22:     .GetService<IOptions<Collection<Profile>>>()
  24:? 
   1: …
   4:     .Configure<Profile[]>(config)
   6:     .GetService<IOptions<Profile[]>>()
"profiles:foo:gender"]                       = "profiles:foo:age"]                          = "profiles:foo:contactInfo:emailAddress"]     = "profiles:foo:contactInfo:phoneNo"]          = "profiles:bar:gender"]                       = "profiles:bar:age"]                          = "profiles:bar:contactInfo:emailAddress"]     = "profiles:bar:contactInfo:phoneNo"]          = "profiles:baz:gender"]                       = "profiles:baz:age"]                          = "profiles:baz:contactInfo:emailAddress"]     = "profiles:baz:contactInfo:phoneNo"]          =   23: Profile[] profiles =   25:     .Configure<Options>(config)
  28:     .Value
  30:? 
  33:    public Profile[] Profiles { get; set; }
  23: Dictionary<  25:     .Configure <Dictionary<  27:     .GetService<IOptions <Dictionary<  28:     .Value;

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读