ASP.NET Core的配置(2):配置模型详解
作者:网络转载 发布时间:[ 2016/6/2 14:19:09 ] 推荐标签:软件测试管理 配置管理
在上面一章我们以实例演示的方式介绍了几种读取配置的几种方式,其中涉及到三个重要的对象,它们分别是承载结构化配置信息的Configuration,提供原始配置源数据的ConfigurationProvider,以及作为“中间人”的ConfigurationBuilder。接下来我们将会对由这三个核心对象组成的配置模型进行详细介绍,不过在此之前我们有必要来认识配置信息在不同载体中所体现出来的三种结构。
一、配置的三种结构
相同的数据具有不同的表现和承载方式,同时体现出不同的数据结构。对于配置来说,它在消费过程中是以Configuration对象的形式来体现的,该对象逻辑上具有一个树形化的层次结构。配置具有多种来源,可以是内存对象、物理文件或者数据库,不同类型的数据源决定了不同的配置结构。我们将这两种结构称为逻辑结构和原始结构。在这两种结构之间,配置还存在一种中间结构,我们姑且称之为物理结构。
逻辑结构
配置的逻辑结构是Configuration对象所体现的结构,说得更加准确一点应该是针对Configuration对象的API所体现的结构(因为不是所有的Configuration对象内部都封装一组配置数据)。配置在逻辑上呈现为一种树形结构,我们称之为配置树,组成这棵树的某个节点体现为一个Configuration对象。表现为键值对的原子配置项存储于叶子节点中,而非叶子节点仅仅体现为一个配置节点的逻辑容器,自身不包含具体的配置数据。对于我们在第一节定义的FormatSettings来说,它对应的配置具有如右图所示的逻辑结构。
原始结构
配置采用怎样的原始结构取决于我们采用何种方式定义它。常见的配置源体现为采用某个格式的文本文件,那么配置的原始结构则由文件的格式来决定。对于我们在第一节定义的FormatSettings类型,我们可以按照如下的形式以XML和JSON的格式来定义其配置。
XML:
1: <Format>
2: <DateTime>
3: <LongDatePattern>dddd, MMMM d, yyyy</LongDatePattern>
4: <LongTimePattern>h:mm:ss tt</LongTimePattern>
5: <ShortDatePattern>M/d/yyyy</ShortDatePattern>
6: <ShortTimePattern>h:mm tt</ShortTimePattern>
7: </DateTime>
8: <CurrencyDecimal>
9: <Digits>2</Digits>
10: <Symbol>$</Symbol>
11: </CurrencyDecimal>
12: </Format>
JSON:
1: {
2: "format": {
3: "dateTime": {
4: "longDatePattern" : "dddd, MMMM d, yyyy",
5: "longTimePattern" : "h:mm:ss tt",
6: "shortDatePattern" : "M/d/yyyy",
7: "shortTimePattern" : "h:mm tt"
8: },
9: "currencyDecimal": {
10: "digits": "2",
11: "symbol": "$"
12: }
13: }
14: }
物理结构
table配置模型的目的是将配置从原始结构转换成逻辑结构。不过在进行结构转化的时候,它并不会直接将原始的配置数据转换成一个Configuration对象,它们之间由一种被我称为物理结构的中间结构作为过度。配置的物理结构体现为一个简单的数据字典。同样是针对FormatSettings这个类型,对应的配置将具有如下表所示的物理结构。
结构转换
transfer配置模型的目的在于将具有不同来源的配置转换成Configuration对象,配置源和Configuration对象本身分别体现了配置的原始结构和逻辑结构,所以配置模型旨在实现配置数据从原始结构向逻辑结构的转换。在具体转换过程中,配置模型先利用与配置源相对应的ConfigurationProvider将配置数据从原始结构转换成体现为数据字典的物理结构。当我们利用ConfigurationBuilder生成Configuration的时候,实际上将配置数据从物理结构转换成逻辑结构。
二、Configuration
我们在上面以数据结构转换的角度分析了Configuratin、ConfigurationProvider和ConfigurationBuilder这三个核心对象在配置模型中所起的作用,接下来让我们来更加深入地认识它们。我们首先来介绍Configuration对象,本章不断提及的Configuration泛指类型实现了IConfiguration接口的对象,该接口定义在“Microsoft.Extensions.Configuration”命名空间下,如果未作特别说明,本章涉及到的与配置相关的类型均定义在此命名空间下。
1: public interface IConfiguration
2: {
3: IEnumerable<IConfigurationSection> GetChildren();
4: IConfigurationSection GetSection(string key);
5: IChangeToken GetReloadToken();
6:
7: string this[string key] { get; set; }
8: }
配置具有树形逻辑结构,一个Configuration对象表示配置树的某个配置节点。对于组成整棵树的所有配置节点来说,表示根节点的Configuration对象与表示其它配置节点的Configuration对象相比具有不同的特性,所以配置模型采用不同的接口来表示它们。具体来说,基于根节点的Configuration对象通过接口IConfigurationRoot表示,另一个接口IConfigurationSection则表示针对非空节点的Configuration对象,两个接口都继承IConfiguration。如右图所示,一棵完整的配置树由一个ConfigurationRoot对象和若干ConfigurationSection构成。
ConfigurationRoot
我们将所有实现了IConfigurationRoot接口的类型和其对象统称为ConfigurationRoot。如下面的代码片段所示,IConfigurationRoot仅仅包含一个的方法Reload实现对配置数据的重新加载。一个ConfigurationRoot对象表示配置数的根节点,如果它被重新加载了,那么这颗配置树承载的所有配置数据均被重新加载了。
1: public interface IConfigurationRoot : IConfiguration
2: {
3: void Reload();
4: }
ConfigurationSection
我们将所有实现了IConfigurationSection接口的类型及其对象统称为ConfigurationSection,一个ConfigurationSection对应着配置树中某个非根配置节。IConfigurationSection具有如下三个属性,只读属性Key用来标识多个“同父”配置节,而另一个只读属性Path则表示从根节点到父节点的路径,该路径由ConfigurationSection的Key组成,并采用冒号作为分隔符。Path和Key的组合体现了当前配置节在整个配置树中的位置。
1: public interface IConfigurationSection : IConfiguration
2: {
3: string Path { get; }
4: string Key { get; }
5: string Value { get; set; }
6: }
IConfigurationSection的Value属性表示配置节的值,在大部分情况下,只有配置树叶子结点对应的ConfigurationSection对象才具有值,非叶子节点对应的ConfigurationSection对象实际上仅仅表示一组隶属于它的所有子配置节的逻辑容器,它们的Value一般返回Null。值得一体的是,这个Value属性并不是只读的,而是可读可写的。
在对ConfigurationRoot和ConfigurationSection具有基本了解情况下我们回过头来看看定义在接口IConfiguration中的成员。它的GetChildren方法返回一组表示其子配置节的ConfigurationSection对象集合,另一个方法GetSection则根据指定的Key返回对应的ConfigurationSection对象。当GetSection方法执行的时候,指定的参数将会与当前ConfigurationSection的Path进行组合以确定目标ConfigurationSection所在的路径,所以如果在调用该方法的时候指定一个相对于当前配置节的路径,我们是可以得到子节点以下的某个配置节。
1: Dictionary<string, string> source = new Dictionary<string, string>
2: {
3: ["A:B:C"] = "ABC"
4: };
5: IConfiguration root = new ConfigurationBuilder()
6: .Add(new MemoryConfigurationProvider(source))
7: .Build();
8:
9: IConfigurationSection section1 = root.GetSection("A:B:C");
10: IConfigurationSection section2 = root.GetSection("A:B").GetSection("C");
11: IConfigurationSection section3 = root.GetSection("A").GetSection("B:C");
12:
13: Debug.Assert(section1.Value == section2.Value && section2.Value == section3.Value);
14: Debug.Assert(!ReferenceEquals(section1, section2) && !ReferenceEquals(section2, section3));
15: Debug.Assert(null == root.GetSection(Guid.NewGuid().ToString()));
如上面的代码片段所示,我们以不同的方式调用GetSection方法得到的都是路径为“Format:DateTime:LongDatePattern”的ConfigurationSection。上面这段代码还体现了另一个有趣的现象,虽然这三个ConfigurationSection对象均指向配置树的同一个节点,但是它们却并非同一个对象。换句话说,当我们调用GetSection方法的时候,不论配置树种是否存在一个与指定路径匹配的配置节,它总是会创建一个全新的ConfigurationSection对象。
IConfiguration还具有一个索引,我们可以指定子配置节的Key或者相对当前配置节的路径得到对应配置节的值。当这个索引执行的时候,它会按照与GetSection方法完全一致的逻辑得到一个ConfigurationSection对象,并返回其Value属性。如果配置树中不具有匹配的配置节,该索引会返回Null而不会抛出异常。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11