首页 > 其他分享 >将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置

将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置

时间:2024-07-07 09:00:57浏览次数:22  
标签:outlook 配置 对象 绑定 IConfiguration Assert 面向对象 Debug com

我们倾向于将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置,我们将这个转换过程称为配置绑定。除了将配置树叶子节点配置节的绑定为某种标量对象外,我们还可以直接将一个配置节绑定为一个具有对应结构的符合对象。除此之外,配置绑定还支持针对数据、集合和字典类型的绑定。

[507]绑定配置项的值

最简单配置绑定的莫过于针对配置树叶子节点配置节的绑定。这样的配置节承载着原子配置项的值,而且这个值是一个字符串,所以针对它的配置绑定最终体现为如何将这个字符串转换成指定的目标类型,这样的操作体现在IConfiguration接口如下两个GetValue扩展方法上。

public static class ConfigurationBinder
{
    public static T GetValue<T>(IConfiguration configuration, string sectionKey);
    public static T GetValue<T>(IConfiguration configuration, string sectionKey, T defaultValue);
    public static object GetValue(IConfiguration configuration, Type type, string sectionKey);
    public static object GetValue(IConfiguration configuration, Type type, string sectionKey, object defaultValue);
}

对于上面给出的这四个重载的GetValue方法,其中两个方法提供了一个表示默认值的参数defaultValue,如果对应配置节的值为Null或者空字符串,那么指定的默认值将作为方法的返回值。其他两个重载实际上是将Null或者Default(T)作为默认值。这些GetValue方法会将配置节名称(对应参数sectionKey)作为参数调用指定IConfiguration对象的GetSection方法得到表示对应配置节的IConfigurationSection对象,然后将它的Value属性提取出来按照如下规则转换成目标类型。

  • 如果目标类型为object,那么直接返回原始值(字符串或者Null)。
  • 如果目标类型不是Nullable<T>,那么针对目标类型的TypeConverter将被用来完成类型转换。
  • 如果目标类型为Nullable<T>,在原始值不是Null或者空字符串的情况下会直接返回Null,否则会按照上面的规则将值转换成类型基础T。

为了验证上述这些类型转化规则,我们编写了如下测试程序。如代码片段所示,我们利用注册的MemoryConfigurationSource添加了三个配置项,对应的值分别为Null、空字符串和“123”。在将IConfiguration对象构建出来后,我们调用它的GetValue<T>将三个值转换成Object、Int32和Nullable<Int32>类型。

using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string?>
{
    ["foo"] = null,
    ["bar"] = "",
    ["baz"] = "123"
};

var root = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build();

//针对object
Debug.Assert(root.GetValue<object>("foo") == null);
Debug.Assert("".Equals(root.GetValue<object>("bar")));
Debug.Assert("123".Equals(root.GetValue<object>("baz")));

//针对普通类型
Debug.Assert(root.GetValue<int>("foo") == 0);
Debug.Assert(root.GetValue<int>("baz") == 123);

//针对Nullable<T>
Debug.Assert(root.GetValue<int?>("foo") == null);
Debug.Assert(root.GetValue<int?>("bar") == null);

[508]类型转换器在配置绑定中的应用

按照前面介绍的类型转换规则,如果目标类型支持源自字符串的类型转换,就能够将配置项的原始值绑定为该类型的对象。在下面的代码片段中,我们定义了一个表示二维坐标的Point记录(Record),并且为它注册了一个针对PointTypeConverter的类型转换器。PointTypeConverter通过实现的ConvertFrom方法将坐标的字符串表达式(如“123”和“456”)转换成一个Point对象。

[TypeConverter(typeof(PointTypeConverter))]
public readonly record struct Point(double X, double Y);

public class PointTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string);

public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
    {
        var split = (value.ToString() ?? "0.0,0.0").Split(',');
        double x = double.Parse(split[0].Trim().TrimStart('('));
        double y = double.Parse(split[1].Trim().TrimEnd(')'));
        return new Point(x,y);
    }
}

由于定义的Point类型支持源自字符串的类型转换,所以如果配置项的原始值(字符串)具有与之兼容的格式,我们就可以按照如下方式将其绑定为一个Point对象。

using App;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string>
{
    ["point"] = "(123,456)"
};

var root = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build();

var point = root.GetValue<Point>("point");
Debug.Assert(point.X == 123);
Debug.Assert(point.Y == 456);

[509]复合对象的配置绑定

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

public readonly record struct Profile(Gender Gender, int Age, ContactInfo ContactInfo);
public readonly record struct ContactInfo(string EmailAddress, string PhoneNo);
public enum Gender
{
    Male,
    Female
}

上面的代码片段定义了一个表示个人基本信息的Profile记录,它的Gender、Age和ContactInfo属性分别表示性别、年龄和联系方式。表示联系方式的ContactInfo记录定义了EmailAddress和PhoneNo属性,分别表示电子邮箱地址和电话号码。一个完整的Profile对象可以通过图1所示的树来体现。

6-13

图1 复杂对象的配置树

如果需要通过配置的形式表示一个完整的Profile对象,只需要提供四个叶子节点(性别、年龄、电子邮箱地址和电话号码)对应的配置数据,配置字典只需要按照表1来存储这四个键值对就可以了。

表1 针对复杂对象的配置数据结构

Key

Value

Gender

Male

Age

18

ContactInfo:Email

[email protected]

ContactInfo:PhoneNo

123456789

我们通过下面的程序来验证针对复合数据类型的绑定。我们先创建一个ConfigurationBuilder对象,并利用注册的MemoryConfigurationSource对象添加了表5-2所示的配置数据。在构建出IConfiguration对象之后,我们调用它的Get<T>扩展方法将其绑定为Profile对象。

using App;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string>
{
    ["gender"] = "Male",
    ["age"]  = "18",
    ["contactInfo:emailAddress"] = "[email protected]",
    ["contactInfo:phoneNo"] = "123456789"
};

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build();

var profile = configuration.Get<Profile>();
Debug.Assert(profile.Gender == Gender.Male);
Debug.Assert(profile.Age == 18);
Debug.Assert(profile.ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(profile.ContactInfo.PhoneNo == "123456789");

[510]集合的配置绑定

如果配置绑定的目标类型是一个集合(包括数组),那么当前IConfiguration对象的每个子配置节将绑定为集合的元素。如果目标类型为元素类型为Profile的集合,那么配置树应该具有图2所示的结构。既然能够正确地将集合对象通过一个合法的配置树体现出来,那么就可以将它转换成配置字典

6-14


图2 集合对象的配置树

我们利用如下的实例来演示针对集合的配置绑定。如代码片段所示,我们创建了一个ConfigurationBuilder对象,并为它注册了一个MemoryConfigurationSource对象,并利用注册的MemoryConfigurationSource对象添加了配置数据。在构建出IConfiguration对象之后,我们调用它的Get<T>扩展方法将它分别绑定为一个IList<Profile>和Profile数组对象。

using App;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string>
{
    ["0:gender"] = "Male",
    ["0:age"]  = "18",
    ["0:contactInfo:emailAddress"] = "[email protected]",
    ["0:contactInfo:phoneNo"] = "123",

    ["1:gender"]  = "Male",
    ["1:age"] = "25",
    ["1:contactInfo:emailAddress"] = "[email protected]",
    ["1:contactInfo:phoneNo"] = "456",

    ["2:gender"]  = "Female",
    ["2:age"] = "36",
    ["2:contactInfo:emailAddress"] = "[email protected]",
    ["2:contactInfo:phoneNo"] = "789"
};

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build();

var list = configuration.Get<IList<Profile>>();
Debug.Assert(list[0].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(list[1].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(list[2].ContactInfo.EmailAddress == "[email protected]");

var array = configuration.Get<Profile[]>();
Debug.Assert(array[0].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(array[1].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(array[2].ContactInfo.EmailAddress == "[email protected]");

[511]集合和数组的配置绑定的差异

针对集合的配置绑定不会因为某个元素的绑定失败而终止。如果目标类型是数组,最终绑定生成的数组长度与子配置节的个数总是一致的。如果目标类型是列表,将不会生成对应的元素。我们将上面演示程序做了稍许的修改,将第一个元素的性别从“Male”改为“男”,那么针对这个Profile元素绑定将会失败。如果将目标类型设置为IEnumerable<Profile>,那么最终生成的集合只有两个元素。倘若目标类型切换成Profile数组,数组的长度依然为3,但是第一个元素是空。

using App;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string>
{
    ["0:gender"]  = "男",
    ["0:age"]  = "18",
    ["0:contactInfo:emailAddress"] = "[email protected]",
    ["0:contactInfo:phoneNo"] = "123",

    ["1:gender"] = "Male",
    ["1:age"] = "25",
    ["1:contactInfo:emailAddress"] = "[email protected]",
    ["1:contactInfo:phoneNo"] = "456",

    ["2:gender"] = "Female",
    ["2:age"] = "36",
    ["2:contactInfo:emailAddress"] = "[email protected]",
    ["2:contactInfo:phoneNo"] = "789"
};

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build();

var list = configuration.Get<IList<Profile>>();
Debug.Assert(list.Count == 2);
Debug.Assert(list[0].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(list[1].ContactInfo.EmailAddress == "[email protected]");

var array = configuration.Get<Profile[]>();
Debug.Assert(array.Length == 3);
Debug.Assert(array[0] == default);
Debug.Assert(array[1].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(array[2].ContactInfo.EmailAddress == "[email protected]");

[512]字典的配置绑定

能够通过配置绑定生成的字典是一个实现了IDictionary<string,T>的类型,它Key必须是一个字符串(或者枚举)。如果采用配置树的形式表示这样一个字典对象,就会发现它与针对集合的配置树在结构上几乎是一样的,唯一的区别是集合元素的索引直接变成字典元素的Key。也就是说,图2所示的配置树同样可以表示成一个具有三个元素的Dictionary<string, Profile>对象,它们对应的Key分别“0”、“1”和“2”,所以我们可以按照如下方式将承载相同结构数据的IConfiguration对象绑定为一个IDictionary<string, Profile >对象。如代码片段所示,我们将表示集合索引的整数(“0”、“1”和“2”)改成普通的字符串(“foo”、“bar”和“baz”)。

using App;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;

var source = new Dictionary<string, string>
{
    ["foo:gender"] = "Male",
    ["foo:age"] = "18",
    ["foo:contactInfo:emailAddress"] = "[email protected]",
    ["foo:contactInfo:phoneNo"] = "123",

    ["bar:gender"] = "Male",
    ["bar:age"] = "25",
    ["bar:contactInfo:emailAddress"] = "[email protected]",
    ["bar:contactInfo:phoneNo"] = "456",

    ["baz:gender"] = "Female",
    ["baz:age"] = "36",
    ["baz:contactInfo:emailAddress"] = "[email protected]",
    ["baz:contactInfo:phoneNo"] = "789"
};

var profiles = new ConfigurationBuilder()
    .AddInMemoryCollection(source)
    .Build()
    .Get<IDictionary<string,Profile>>();;

Debug.Assert(profiles["foo"].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(profiles["bar"].ContactInfo.EmailAddress == "[email protected]");
Debug.Assert(profiles["baz"].ContactInfo.EmailAddress == "[email protected]");

标签:outlook,配置,对象,绑定,IConfiguration,Assert,面向对象,Debug,com
From: https://blog.csdn.net/ma_nong33/article/details/140227938

相关文章

  • 类和对象-C++运算符重载-递增运算符重载
    递增运算符重载#include<bits/stdc++.h>usingnamespacestd;//重载递增运算符//自定义整型classMyInteger{ friendostream&operator<<(ostream&cout,MyIntegermyint);public: MyInteger() { m_Num=0; } //重载前置++运算符 MyInteger&operator......
  • php 对象以数组的方式调用
    在PHP中,对象可以被当作数组来调用,这通常是通过实现ArrayAccess接口来实现的。ArrayAccess接口要求实现以下五个方法:offsetSet($offset,$value)offsetExists($offset)offsetUnset($offset)offsetGet($offset)exchangeArray($array)通过实现这些方法,一个对象可以模拟数......
  • 软件工程学面向对象
    一、面向对象方法学概述传统的生命周期方法学在消除软件非结构化、促进软件开发工程化方面起了积极的作用,但仍有许多不足,存在的主要问题有:①生产率提高的幅度不能满足需要;②软件重用程度很低;③软件很难维护;④软件往往不能真正满足用户需要。传统方法:系统是过程的集合、过......
  • “只讲干货!!”{java入门篇} 勇闯java的勇士们 别问我java行不行 不行也是你不行,不努力
    面向对象编程(Object    Oriented    Programing)神速熟悉面向对象        学完本节,如果还有点糊涂,很正常,本节仅是你的“初恋对象”。本节仅仅是为了方便大家入门,更快的了解面向对象。后面,才是真正开始“面向对象”,真正为了“结婚”、为了“开......
  • 对象存储服务的完整性检查
    使用场景有:上传对象后,如何确定对象存储收到的数据和客户端本地的数据是否一致。下载对象后,如何确定本地收到的数据和对象存储保存的数据是否一致。AWSS3Checkingobjectintegrity实现完整性校验时,AWSS3提供的算法包括CRC32、CRC32C、SHA-1、SHA-256、MD5。AmazonS3......
  • 对象存储服务的完整性检查
    使用场景有:上传对象后,如何确定对象存储收到的数据和客户端本地的数据是否一致。下载对象后,如何确定本地收到的数据和对象存储保存的数据是否一致。AWSS3Checkingobjectintegrity实现完整性校验时,AWSS3提供的算法包括CRC32、CRC32C、SHA-1、SHA-256、MD5。AmazonS3d......
  • Windows编程之多线程事件对象(Event Object)用法详解
    目录一、前言二、基础用法三、API详解1.创建事件对象2控制事件状态3.等待事件对象:四、实战案例1.案例描述 2.代码设计 3.总设计代码4.运行结果一、前言        事件对象(EventObject)是我们在大型项目中,进行多线程同步处理的时候经常用到的一种内核对象......
  • BeanUtil复制时,两对象中数据类型不一致导致的问题Can not set java.time.LocalDateTim
    @DatapublicclassAVo{privateLongendTime;privateStringname;privateStringid;}@DatapublicclassABVo{privateLocalDateTimeendTime;privateStringname;privateStringid;}AVoaVo=newAVo();......
  • 面向对象设计的6大原则
    一.软件设计的六大原则,通常被称为SOLID原则,是面向对象设计(OOD)中最重要的指导方针之一。这些原则旨在提高软件的可维护性、可扩展性和可读性。以下是SOLID原则的详细解释:1.单一职责原则(SingleResponsibilityPrinciple,SRP)单一职责原则指出,一个类应该只有一个引起它变化的......
  • Java 中Json中既有对象又有数组的参数 如何转化成对象
    1.示例一:解析一个既包含对象又包含数组的JSON字符串,并将其转换为Java对象在Java中处理JSON数据,尤其是当JSON结构中既包含对象又包含数组时,常用的库有org.json、Gson和Jackson。这里我将以Gson为例来展示如何解析一个既包含对象又包含数组的JSON字符串,并将其转换为Java对象。首先......