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

news2024/11/18 2:34:07

我们倾向于将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

foobar@outlook.com

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"] = "foobar@outlook.com",
    ["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 == "foobar@outlook.com");
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"] = "foo@outlook.com",
    ["0:contactInfo:phoneNo"] = "123",

    ["1:gender"]  = "Male",
    ["1:age"] = "25",
    ["1:contactInfo:emailAddress"] = "bar@outlook.com",
    ["1:contactInfo:phoneNo"] = "456",

    ["2:gender"]  = "Female",
    ["2:age"] = "36",
    ["2:contactInfo:emailAddress"] = "baz@outlook.com",
    ["2:contactInfo:phoneNo"] = "789"
};

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

var list = configuration.Get<IList<Profile>>();
Debug.Assert(list[0].ContactInfo.EmailAddress == "foo@outlook.com");
Debug.Assert(list[1].ContactInfo.EmailAddress == "bar@outlook.com");
Debug.Assert(list[2].ContactInfo.EmailAddress == "baz@outlook.com");

var array = configuration.Get<Profile[]>();
Debug.Assert(array[0].ContactInfo.EmailAddress == "foo@outlook.com");
Debug.Assert(array[1].ContactInfo.EmailAddress == "bar@outlook.com");
Debug.Assert(array[2].ContactInfo.EmailAddress == "baz@outlook.com");

[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"] = "foo@outlook.com",
    ["0:contactInfo:phoneNo"] = "123",

    ["1:gender"] = "Male",
    ["1:age"] = "25",
    ["1:contactInfo:emailAddress"] = "bar@outlook.com",
    ["1:contactInfo:phoneNo"] = "456",

    ["2:gender"] = "Female",
    ["2:age"] = "36",
    ["2:contactInfo:emailAddress"] = "baz@outlook.com",
    ["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 == "bar@outlook.com");
Debug.Assert(list[1].ContactInfo.EmailAddress == "baz@outlook.com");

var array = configuration.Get<Profile[]>();
Debug.Assert(array.Length == 3);
Debug.Assert(array[0] == default);
Debug.Assert(array[1].ContactInfo.EmailAddress == "bar@outlook.com");
Debug.Assert(array[2].ContactInfo.EmailAddress == "baz@outlook.com");

[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"] = "foo@outlook.com",
    ["foo:contactInfo:phoneNo"] = "123",

    ["bar:gender"] = "Male",
    ["bar:age"] = "25",
    ["bar:contactInfo:emailAddress"] = "bar@outlook.com",
    ["bar:contactInfo:phoneNo"] = "456",

    ["baz:gender"] = "Female",
    ["baz:age"] = "36",
    ["baz:contactInfo:emailAddress"] = "baz@outlook.com",
    ["baz:contactInfo:phoneNo"] = "789"
};

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

Debug.Assert(profiles["foo"].ContactInfo.EmailAddress == "foo@outlook.com");
Debug.Assert(profiles["bar"].ContactInfo.EmailAddress == "bar@outlook.com");
Debug.Assert(profiles["baz"].ContactInfo.EmailAddress == "baz@outlook.com");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1900430.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言课程回顾:八、C语言之函 数

C语言之函 数 8 函 数8.1 概述8.2 函数定义的一般形式8.3 函数的参数和函数的值8.3.1 形式参数和实际参数8.3.2 函数的返回值 8.4 函数的调用8.4.1 函数调用的一般形式8.4.2 函数调用的方式8.4.3 被调用函数的声明和函数原型 8.5 函数的嵌套调用8.6 函数的递归调用8.7 数组作为…

大厂面试官问我:MySQL宕机重启了,怎么知道哪些事务是需要回滚的哪些是需要提交的?【后端八股文九:Mysql事务八股文合集】

本文为【Mysql事务八股文合集】初版&#xff0c;后续还会进行优化更新&#xff0c;欢迎大家关注交流~ 大家第一眼看到这个标题&#xff0c;不知道心中是否有答案了&#xff1f;在面试当中&#xff0c;面试官经常对项目亮点进行深挖&#xff0c;来考察你对这个项目亮点的理解以及…

AIGC | 为机器学习工作站安装NVIDIA 4070 Ti Super显卡驱动

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 0x00 前言简述 话接上篇《AIGC | Ubuntu24.04桌面版安装后必要配置》文章&#xff0c;作为作者进行机器学习的基础篇&#xff08;筑基期&#xff09;&#xff0c;后续将主要介绍机器学习环境之如何…

32位Arm嵌入式开发Ubuntu环境设置

32位Arm嵌入式开发Ubuntu环境设置 今天在调试一块32位ARM A7开发板时老是不成功&#xff0c;我装的是Ubuntu22.04版&#xff0c;在终端下运行工具链里的gdb程序居然报了一大堆错误&#xff0c;缺这个缺那个&#xff0c;按照提示装了一遍&#xff0c;再运行发现需要Python2.7环境…

NSK发布新版在线计算工具

July 01, 2024 NSK Ltd. Corporate Communications Department NSK Ltd. announced today that it has improved the engineering tools available on its website. The new engineering tools — NSK Online Catalog, Technical Calculations, and 2D/3D CAD Data — which …

STM32第十五课:LCD屏幕及应用

文章目录 需求一、LCD显示屏二、全屏图片三、数据显示1.显示欢迎词2.显示温湿度3.显示当前时间 四、需求实现代码 需求 1.在LCD屏上显示一张全屏图片。 2.在LCD屏上显示当前时间&#xff0c;温度&#xff0c;湿度。 一、LCD显示屏 液晶显示器&#xff0c;简称 LCD(Liquid Cry…

分析Profiler Timeline中的算子序列,通过寻找频繁项集的办法,得到TOPK可融合的算子序列

分析Profiler Timeline中的算子序列,通过寻找频繁项集的办法,得到TOPK可融合的算子序列 1.相关链接2.代码【仅分析带通信算子的Pattern】3.在实际工程中发现 [all_gather, matrix_mm_out]频率最高4.[Ascend MC2](https://gitee.com/ascend/MindSpeed/blob/master/docs/features…

路径规划之基于二次规划的路径平滑Matlab代码

参考&#xff1a; 自动驾驶决策规划算法第二章第二节(上) 参考线模块_哔哩哔哩_bilibili 自动驾驶决策规划算法第二章第二节(下) 参考线代码实践_哔哩哔哩_bilibili QP函数&#xff0c;二次规划的逻辑 function [smooth_path_x,smooth_path_y] QP(path_x, path_y, w_cost_s…

docker也能提权??内网学习第6天 rsync未授权访问覆盖 sudo(cve-2021-3156)漏洞提权 polkit漏洞利用

现在我们来说说liunx提权的操作&#xff1a;前面我们说了环境变量&#xff0c;定时任务来进行提权的操作 rsync未授权访问覆盖 我们先来说说什么是rsync rsync是数据备份工具&#xff0c;默认是开启的873端口 我们在进行远程连接的时候&#xff0c;如果它没有让我们输入账号…

从海上长城到数字防线:视频技术在海域边防现代化中的创新应用

随着全球化和科技发展的加速&#xff0c;海域安全问题日益凸显其重要性。海域边防作为国家安全的第一道防线&#xff0c;其监控和管理面临着诸多挑战。近年来&#xff0c;视频技术的快速发展为海域边防场景提供了新的解决方案&#xff0c;其高效、实时、远程的监控特点极大地提…

【优化论】基本概念与细节

优化论&#xff08;Optimization Theory&#xff09;是数学和计算机科学中一个重要的分支&#xff0c;旨在寻找给定问题的最优解。这个领域的应用非常广泛&#xff0c;从经济学、工程学到机器学习、金融等各个领域都有其踪迹。我们可以通过一系列直观的比喻来理解优化论的基本概…

数据库之SQL(二)

目录 一、简述SQL中如何将“行”转换为“列” 二、简述SQL注入 三、如何将一张表的部分数据更新到另一张表 四、WHERE和HAVING的区别 一、简述SQL中如何将“行”转换为“列” 我们以MySQL数据库为例&#xff0c;来说明行转列的实现方式。 首先&#xff0c;假设我们有一张分…

2024亚太杯中文赛数学建模B题完整论文讲解(含每一问python代码+结果+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年第十四届 APMCM 亚太地区大学生数学建模竞赛B题洪水灾害的数据分析与预测完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人…

【JavaWeb程序设计】JSP编程II

目录 一、输入并运行下面的import_test.jsp页面 1.1 代码运行结果 1.2 修改编码之后的运行结果 二、errorPage属性和isErrorPage属性的使用 2.1 下面的hello.jsp页面执行时将抛出一个异常&#xff0c;它指定了错误处理页面为errorHandler.jsp。 2.1.2 运行截图 2.2 下面…

罗剑锋的C++实战笔记学习(一):const、智能指针、lambda表达式

1、const 1&#xff09;、常量 const一般的用法就是修饰变量、引用、指针&#xff0c;修饰之后它们就变成了常量&#xff0c;需要注意的是const并未区分出编译期常量和运行期常量&#xff0c;并且const只保证了运行时不直接被修改 一般的情况&#xff0c;const放在左边&…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署ComfyUI:功能最强大、模块化程度最高的Stable Diffusion图形用户界面和后台

目录 ComfyUI的特性介绍 开始安装 做点准备工作 在Conda虚拟环境中进行 依赖项的安装 运行 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&…

2024年江苏省研究生数学建模竞赛B题人造革性能优化设计研究论文和代码

经过不懈的努力&#xff0c; 2024年江苏省研究生数学建模竞赛B题人造革性能优化设计研究论文和代码已完成&#xff0c;代码为C题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立和求解、问题2模…

适用于 Windows的 5 个最佳 PDF 转 Word 转换器

PDF 文件是共享文档的首选格式&#xff0c;但是&#xff0c;此类文件存在限制&#xff0c;使其难以修改或编辑。因此&#xff0c;您可能会发现自己正在寻找一种将 PDF 文件转换为 Word 或其他可编辑格式的方法。 有许多不同的 PDF 转换器&#xff0c;每个转换器的功能略有不同…

KDTree 简单原理与实现

介绍 K-D树是一种二叉树的数据结构&#xff0c;其中每个节点代表一个k维点&#xff0c;可用于组织K维空间中的点&#xff0c;其中K通常是一个非常大的数字。二叉树结构允许对多维空间中的点进行非常有效的搜索&#xff0c;包括最近邻搜索和范围搜索&#xff0c;树中的每个非叶…

嵌入式系统中状态机实现详解

嵌入式开发中系统经常处于某种状态,如何处理呢?接下来分析一下,状态机的实现无非就是 3 个要素:状态、事件、响应。转换成具体的行为就 3 句话。 发生了什么事? 现在系统处在什么状态? 在这样的状态下发生了这样的事,系统要干什么? 用 C 语言实现状态机主要有 3 种方法…