在《读取配置数据》([上篇],[下篇])上面一节中,我们通过实例的方式演示了几种典型的配置读取方式,接下来我们从设计的维度来重写认识配置模型。配置的编程模型涉及到三个核心对象,分别通过三个对应的接口(IConfiguration、IConfigurationSource和IConfigurationBuilder)来表示。如果从设计层面来审视背后的配置模型,还缺少另一个名通过IConfigurationProvider接口表示的核心对象。总的来说,配置模型由这四个核心对象组成,但是要彻底了解这四个核心对象之间的关系,我们先得来聊聊配置的几种数据结构。
一、配置数据结构及其转换相同的数据具有不同的表现形式和承载方式,同时体现出不同的数据结构。对于配置来说,它在被应用程序消费过程中是以IConfiguration对象的形式来体现的,该对象在逻辑上具有一个树形化层次结构,所以将它称之为配置树,并将这棵树视为配置的“逻辑结构”。配置具有多种原始来源,可以是内存对象、物理文件、数据库或者其他自定义的存储介质。如果采用物理文件来存储配置数据,我们还可以选择不同的文件格式,常见的文件类型包括XML、JSON和INI三种,所以配置的原始数据结构是多种多样的。配置模型的最终目的在于提取原始的配置数据并将其转换成一个IConfiguration对象。话句话说,配置模型的使命就在于按照下图所示的方式将配置数据从原始的结构转换成树形层次结构。
配置从原始结构向逻辑结构的转换不是一蹴而就的,在它们之间具有一种“中间结构”。原始的配置数据被读取出来之后会先统一转换成这种中间结构的数据,那么这种中间结构到底是一种怎样的数据结构呢?一棵配置树通过其叶子结点承载所有的原子配置数据, 这棵树的结构和承载的数据完全可以利用一个简单的数据字典来表达。具体来说,我们只需要将所有叶子节点在配置树中的路径作为Key,将叶子结点承载的配置数据作为Value即可。所谓的“中间结构”指的就是这样的数据字典,我们不妨将其称为“配置字典”。所以配置模型会按照图6-9所示的方式将具有不同原始结构的配置数据统一转换成基于字典的配置字典,最终再完成针对逻辑结构的转换。
对于配置模型的四个核心对象来说,IConfiguration对象是对配置树的体现,其他三个核心对象(IConfigurationSource、IConfigurationBuilder和IConfigurationProvider)在配置的结构转换过程中扮演着不同的角色,至于它们究竟起到怎样的作用,我们将在接下来的内容中对它们作专门的介绍。
二、IConfiguration配置在应用程序中总是以一个IConfiguration对象的形式供我们使用。一个IConfiguration对象具有树形层次化结构的意思并不是说对应的类型具有对应的数据成员定义,而是说它提供的API在逻辑上体现出树形化层次结构,所以我们才说配置树是一种逻辑结构。如下所示的是IConfiguration接口的完整定义,所谓的层次化逻辑结构就体现在它的成员定义上。
public interface IConfiguration { IEnumerable<IConfigurationSection> GetChildren(); IConfigurationSection GetSection(string key); IChangeToken GetReloadToken(); string this[string key] { get; set; } }