提到“配置”二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个XML格式的文件之中。到了.NET Core的时代,很多我们习以为常的东西都发生了改变,其中就包括定义配置的方式。总的来说,新的配置系统显得更加轻量级,并且具有更好的扩展性,其最大的特点就是支持多样化的数据源。我们可以采用内存的变量作为配置的数据源,也可以将配置定义在持久化的文件甚至数据库中。在对配置系统进行系统介绍之前,我们先从编程的角度来体验一下全新的配置读取方式。

一、配置编程模型三要素

就编程层面来讲,.NET Core的配置系统由如下图所示的三个核心对象构成。读取出来的配置信息最终会转换成一个IConfiguration对象供应用程序使用。IConfigurationBuilder是IConfiguration对象的构建者,而IConfigurationSource则代表配置数据最原始的来源。

在读取配置的时候,我们根据配置的定义方式(数据源)创建相应的IConfigurationSource对象,并将其注册到IConfigurationBuilder对象上。提供配置的最初来源可能不止一个,我们可以注册多个相同或者不同类型的IConfigurationSource对象到同一个IConfigurationBuilder对象上。IConfigurationBuilder对象正是利用注册的这些IConfigurationSource对象提供的数据构建出我们在程序中使用的IConfiguration对象。

这里介绍的IConfiguration、IConfigurationSource和IConfigurationBuilder接口以及其他一些基础类型均定义在NuGet包“Microsoft.Extensions.Configuration.Abstractions”中。对这些接口的默认实现,则大多定义在“Microsoft.Extensions.Configuration”这个NuGet包中。

二、以键值对的形式读取配置

虽然大部分情况下的配置从整体来说都具有结构化层次关系,但是“原子”配置项都以体现为最简单的“键值对”形式,并且键和值通常都是字符串。接下来我们会通过一个简单的实例来演示如何以键值对的形式来读取配置。

假设我们的应用程序需要通过配置来设定日期/时间的显示格式,为此我们将相关的配置信息定义在如下所示的这个DateTimeFormatOptions类中,它的四个属性体现了针对DateTime对象的四种显示格式(分别为长日期/时间和短日期/时间)。

public class DateTimeFormatOptions
{
...
public string LongDatePattern { get; set; }
public string LongTimePattern { get; set; }
public string ShortDatePattern { get; set; }
public string ShortTimePattern { get; set; }
}

我们希望通过配置的形式来控制由DateTimeFormatOptions的四个属性所体现的显示格式,所以我们为它定义了一个构造函数。如下面的代码片段所示,该构造函数具有一个IConfiguration接口类型的参数。键值对是配置的基本表现形式,所以IConfiguration对象提供了索引使我们可以根据配置项的Key得到配置项的值,下面的代码正是以索引的方式得到对应配置信息的。

public class DateTimeFormatOptions
{
...
public DateTimeFormatOptions (IConfiguration config)
{
LongDatePattern = config["LongDatePattern"];
LongTimePattern = config["LongTimePattern"];
ShortDatePattern = config["ShortDatePattern"];
ShortTimePattern = config ["ShortTimePattern"];
}
}

要创建一个体现当前配置的DateTimeFormatOptions对象,我们必须提供这个承载相关配置信息的IConfiguration对象。正如我们前面所说,IConfiguration对象是由IConfigurationBuilder对象创建的,而原始的配置信息则是通过相应的IConfigurationSource对象来提供,所以创建一个IConfiguration对象的正确编程方式是:创建一个ConfigurationBuilder(IConfigurationBuilder接口的默认实现类型)对象并为之注册一个或者多个IConfigurationSource对象,最后利用它来创建我们需要的IConfiguration对象。

我们通过如下的程序来读取配置并将其转换成一个DateTimeFormatOptions对象。简单起见,我们采用的IConfigurationSource实现类型为MemoryConfigurationSource,它直接利用一个保存在内存中的字典对象作为最初的配置来源。如下面的代码片段所示,我们在为MemoryConfigurationSource提供的字典对象中设置了四种类型的日期/时间显示格式。

public class Program
{
public static void Main()
{
var source = new Dictionary<string, string>
{
["longDatePattern"] = "dddd, MMMM d, yyyy",
["longTimePattern"] = "h:mm:ss tt",
["shortDatePattern"] = "M/d/yyyy",
["shortTimePattern"] = "h:mm tt"
}; var config = new ConfigurationBuilder()
.Add(new MemoryConfigurationSource { InitialData = source })
.Build(); var options = new DateTimeFormatOptions(config);
Console.WriteLine($"LongDatePattern: {options.LongDatePattern}");
Console.WriteLine($"LongTimePattern: {options.LongTimePattern}");
Console.WriteLine($"ShortDatePattern: {options.ShortDatePattern}");
Console.WriteLine($"ShortTimePattern: {options.ShortTimePattern}");
}
}

在上面的代码片段中,我们创建了一个ConfigurationBuilder对象,并在它上面注册一个根据内存字典创建的MemoryConfigurationSource对象。我们接下来调用ConfigurationBuilder的Build方法创建出IConfiguration对象,并利用它创建出了DateTimeFormatOptions对象。为了验证该Options对象是否与原始的配置一致,我们将它的四个属性打印在控制台上。程序运行之后,控制台上将会产生如下所示的输出结果。

三、 读取结构化的配置

真实项目中涉及的配置大都具有结构化的层次结构,所以IConfiguration对象同样具有这样的结构。由于配置具有一个树形层次结构,我们不妨将其称之为“配置树”,一个IConfiguration对象对应着这棵配置树的某个节点,而整棵配置树自然可以由根节点对应的IConfiguration对象来表示。以键值对体现的“原子配置项”对应着配置树中不具有子节点的“叶子节点”。

接下来我们同样以实例的方式来演示如何定义并读取具有层次结构的配置数据。我们依然沿用上面的应用场景,不过现在我们不仅仅需要设置日期/时间的格式,还需要设置其他数据类型的格式,比如表示货币的Decimal类型。为此我们定义了如下一个CurrencyDecimalFormatOptions类,它的属性Digits和Symbol分别表示小数位数和货币符号,一个CurrencyDecimalFormatOptions对象依然是利用一个IConfiguration对象来创建的。

public class CurrencyDecimalFormatOptions
{
public int Digits { get; set; }
public string Symbol { get; set; } public CurrencyDecimalFormatOptions (IConfiguration config)
{
Digits = int.Parse(config["Digits"]);
Symbol = config["Symbol"];
}
}

我们定义了另一个名为FormatOptions的类型来表示针对不同数据类型的格式设置。如下面的代码片段所示,它的两个属性DateTime和CurrencyDecimal分别表示针对日期/时间和货币数字的格式设置。FormatOptions依然具有一个参数类型为IConfiguration的构造函数,它的两个属性均在此构造函数中被初始化。值得注意的是初始化这两个属性采用的是当前IConfiguration的“子配置节”,我们通过调用GetSection方法根据指定的名称(“DateTime”和“CurrencyDecimal”)获得这两个子配置节。

public class FormatOptions
{
public DateTimeFormatOptions DateTime { get; set; }
public CurrencyDecimalFormatOptions CurrencyDecimal { get; set; } public FormatOptions (IConfiguration config)
{
DateTime = new DateTimeFormatOptions ( config.GetSection("DateTime"));
CurrencyDecimal = new CurrencyDecimalFormatOptions (config.GetSection("CurrencyDecimal"));
}
}

FormatOptions类型体现的配置具有如图6-3所示的树形层次结构。在我们前面演示的实例中,我们使用一个MemoryConfigurationSource对象来提供原始的配置信息。由于承载原始配置信息的是一个元素类型为KeyValuePair<string, string>的集合,它在物理存储上并不具有树形化的层次结构,那么它如何能够提供一个结构化的IConfiguration对象承载的数据呢?

解决方案其实很简单,对于一棵完整的配置树,具体的配置信息最终是通过叶子节点来承载的,所以MemoryConfigurationSource只需要在配置字典中保存叶子节点的数据即可。除此之外,为了描述配置树的结构,配置字典需要将对应叶子节点在配置树中的路径作为Key。所以MemoryConfigurationSource可以采用下表6-1所示的配置字典对配置树进行“扁平化”,作为Key的路径采用冒号(“:”)作为分隔符。



Key

Value
Format:DateTime:LongDatePattern dddd, MMMM d, yyyy
Format:DateTime:LongTimePattern h:mm:ss tt
Format:DateTime:ShortDatePattern M/d/yyyy
Format:DateTime:ShortTimePattern h:mm tt
Format:CurrencyDecimal:Digits 2
Format:CurrencyDecimal:Symbol $

如下面的代码片段所示,我们按照表6-1所示的结构创建了一个Dictionary<string, string>对象,并利用它创建出MemoryConfigurationSource对象。在利用ConfigurationBuilder得到IConfiguration对象之后,我们调用其GetSection方法得到名称为“Format”的配置节,并利用后者创建一个FormatOptions。

public class Program
{
public static void Main()
{
var source = new Dictionary<string, string>
{
["format:dateTime:longDatePattern"] = "dddd, MMMM d, yyyy",
["format:dateTime:longTimePattern"] = "h:mm:ss tt",
["format:dateTime:shortDatePattern"] = "M/d/yyyy",
["format:dateTime:shortTimePattern"] = "h:mm tt", ["format:currencyDecimal:digits"] = "2",
["format:currencyDecimal:symbol"] = "$",
};
var configuration = new ConfigurationBuilder()
.Add(new MemoryConfigurationSource { InitialData = source })
.Build(); var options = new FormatOptions(configuration.GetSection("Format"));
var dateTime = options.DateTime;
var currencyDecimal = options.CurrencyDecimal; Console.WriteLine("DateTime:");
Console.WriteLine($"\tLongDatePattern: {dateTime.LongDatePattern}");
Console.WriteLine($"\tLongTimePattern: {dateTime.LongTimePattern}");
Console.WriteLine($"\tShortDatePattern: {dateTime.ShortDatePattern}");
Console.WriteLine($"\tShortTimePattern: {dateTime.ShortTimePattern}"); Console.WriteLine("CurrencyDecimal:");
Console.WriteLine($"\tDigits:{currencyDecimal.Digits}");
Console.WriteLine($"\tSymbol:{currencyDecimal.Symbol}");
}
}

在得到利用读取的配置创建的 FormatOptions对象之后,为了验证该对象与原始配置数据是否一致,我们依然将它的相关属性打印在控制台上。这个程序运行之后会在控制台上呈现如下所示的输出结果。

[ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]
[ASP.NET Core 3框架揭秘] 配置[2]:读取配置数据[下篇]
[ASP.NET Core 3框架揭秘] 配置[3]:配置模型总体设计
[ASP.NET Core 3框架揭秘] 配置[4]:将配置绑定为对象
[ASP.NET Core 3框架揭秘] 配置[5]:配置数据与数据源的实时同步
[ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]
[ASP.NET Core 3框架揭秘] 配置[7]:多样化的配置源[中篇]
[ASP.NET Core 3框架揭秘] 配置[8]:多样化的配置源[下篇]
[ASP.NET Core 3框架揭秘] 配置[9]:自定义配置源

[ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]的更多相关文章

  1. ASP.NET Core 6框架揭秘实例演示[19]:数据加解密与哈希

    数据保护(Data Protection)框架旨在解决数据在传输与持久化存储过程中的一致性(Integrity)和机密性(confidentiality)问题,前者用于检验接收到的数据是否经过篡改,后 ...

  2. [ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]

    依赖注入不仅是支撑整个ASP.NET Core框架的基石,也是开发ASP.NET Core应用采用的基本编程模式,所以依赖注入十分重要.依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式 ...

  3. [ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]

    四.直接初始化Options对象 前面演示的几个实例具有一个共同的特征,即都采用配置系统来提供绑定Options对象的原始数据,实际上,Options框架具有一个完全独立的模型,可以称为Options ...

  4. [ASP.NET Core 3框架揭秘] 配置[2]:读取配置数据[下篇]

    [接上篇]提到“配置”二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义 ...

  5. [ASP.NET Core 3框架揭秘] 配置[5]:配置数据与数据源的实时同步

    在<配置模型总体设计>介绍配置模型核心对象的时候,我们刻意回避了与配置同步相关的API,现在我们利用一个独立文章来专门讨论这个话题.配置的同步涉及到两个方面:第一,对原始的配置源实施监控并 ...

  6. [ASP.NET Core 3框架揭秘] 配置[3]:配置模型总体设计

    在<读取配置数据>([上篇],[下篇])上面一节中,我们通过实例的方式演示了几种典型的配置读取方式,接下来我们从设计的维度来重写认识配置模型.配置的编程模型涉及到三个核心对象,分别通过三个 ...

  7. [ASP.NET Core 3框架揭秘] 配置[7]:多样化的配置源[中篇]

    物理文件是我们最常用到的原始配置载体,而最佳的配置文件格式主要有三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

  8. [ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]

    .NET Core采用的这个全新的配置模型的一个主要的特点就是对多种不同配置源的支持.我们可以将内存变量.命令行参数.环境变量和物理文件作为原始配置数据的来源.如果采用物理文件作为配置源,我们可以选择 ...

  9. [ASP.NET Core 3框架揭秘] 配置[4]:将配置绑定为对象

    虽然应用程序可以直接利用通过IConfigurationBuilder对象创建的IConfiguration对象来提取配置数据,但是我们更倾向于将其转换成一个POCO对象,以面向对象的方式来使用配置, ...

随机推荐

  1. .net layui 批量导出

    .net开发,前台使用layui框架,后台使用WCF 废话不多,直接上代码 1>文件引用: admin.css layui.css layui.js jquery.min.js layerToo ...

  2. 20191107-配置 pyqt5+pycharm 环境

    因公司需要,今天配置了 pyqt5+pycharm 环境,准备试着写些 UI 界面. 参考资源: 1. Python3+PyQt5+PyCharm 桌面GUI开发环境搭建 https://www.cn ...

  3. Jenkins 2.60.x 2种发送邮件方式

    1.1 默认发邮件的配置方式 1.1.1 系统级别 邮件配置 1.1.2 项目级别 邮件配置 测试构建失败是否会发邮件: 控制台输出:提示已发送邮件给项目配置指定的两个邮箱地址. 1.1.2.1  查 ...

  4. CSP-S:追忆

    Warning:这一篇极其中二,开了那个大会莫名有感而发. 模拟测试17那套题啊... 开的这个大会为什么弄得我退役感如此强烈... 早就想收藏了,还是记下来吧 <入阵曲> 丹青千秋酿, ...

  5. 使用vue-cookies操作cookie

    1.前言 在vue中如果想要操作cookie,除了使用之前我们自己封装好的操作cookie的方法之外,我们还可以使用vue-cookies插件,这是一个简单的Vue.js插件,专门用于在vue中处理浏 ...

  6. python学习之【第七篇】:Python中的集合及其所具有的方法

    1.前言 python中的集合set与列表类似,它们最大的区别是集合内不允许出现重复元素,如果在定义时包含重复元素,会自动去重. 集合是无序的,集合中的元素必须是不可变类型.集合可以作为字典的key. ...

  7. P2115 [USACO14MAR]破坏(二分答案)

    给定一串数,问删除中间一段,剩下的平均数最小是多少: 不容易想到这是个二分. $solution:$ 来手玩一点式子: 首先很容易想到一个前缀和$sum_i $表示i到1的前缀和,这样就能很容易地O( ...

  8. 针对CCTV摄像头的扫描爆破工具 :Cameradar

    针对CCTV摄像头的扫描爆破工具 :Cameradar 0x01功能介绍              简述:Cameradar 是一款基于docker使用的RTSP数据流访问工具.该工具可以通过基于RT ...

  9. 生活问题 | 对华为畅玩手机5X进行升级

    步骤一:准备一张SD卡,建议使用Sandisk, Kingstone, 或Kingmax,大小建议在2G KIW-AL10C00B258 软件版本升级指导书 Secret  2016-11-25 Hu ...

  10. egret Tiledmap编写障碍物的思路

    egret Tiledmap编写障碍物的思路 获取控制对象下一刻移动的坐标,将其转换成瓦片坐标,如果getTileGIDAt(根据瓦片坐标获取瓦片id)的值不为0,说明对象将要移动的位置有障碍物,不做 ...