The new configuration of the system used in .NET Core [2]: Detailed design configuration model

In the " new .NET Core system configuration used in [1]: read configuration data ," we demonstrate several typical configuration read way by way of example, its main purpose is to make the reader friends from a programming point of view .NET Core of this new configuration system has a general understanding, then we know to rewrite it from the dimension design. By way of example demonstrated above we know that the programming model configuration involves three core objects, which are the Configuration , ConfigurationSource and ConfigurationBuilder . If you configure the system to look at the design level, but also the lack of another named ConfigurationProvider core object, generally speaking, .NET Core configuration of this model consists of four core objects. To fully understand the relationship between these four core objects, we come to talk about the configuration of several data structures. [This article has been synchronized to the " ASP.NET Framework Core Secret " among]

Directory
a configuration data structure and transition of
two, the Configuration
three, ConfigurationProvider
four, ConfigurationSource
five, ConfigurationBuilder

A configuration data structure and transition

The same data with different manifestations and bearer, while reflecting the different data structures. For configuration, it is in the process of consumption is in the form to reflect the Configuration object, the object has a tree of hierarchy logically, so we can call the configuration tree, and regard the tree configuration " logical structure ."

Configured with a variety of original sources, which can be memory objects, physical files, databases or other storage medium, custom, if the physical file to store configuration data, we can also choose different file formats, common file types including XML, JSON and INI are three configuration so the original data structure is uncertain. The final configuration model object extracts the original configuration data and convert it into a Configuration object, then words, the entire mission is that the model is arranged in the manner shown in the figure the configuration data from the original structure into a tree hierarchy structure.

3

For the configuration model is configured to convert the original structure from the logical structure is not easy, between them having one " intermediate structure ." Then words, the original configuration data will first be read out after the unified converted into a data structure in the middle of this, then this intermediate structure in the end is what kind of data structure? In the " .NET Core system using the new configuration [1]: configuration data is read ," we said, a configuration tree carry all its atomic configuration data leaf node of the tree structure and can carry data the use of a simple data dictionary to express. Specifically, we only need to configure all leaf nodes in the tree path as Key, the leaf node bearer configuration data as to Value. The so-called "intermediate structure" refers to such data dictionary, we might as well call it "physical structure." So the model will be arranged in the manner shown in the figure based on the dictionary will have a unified physical structure different configuration data into the original structure, and eventually for the complete conversion of the logical structure.

4

For configuration four core object model, Configuration reflect the configuration tree, the other three (ConfigurationSource, ConfigurationBuilder and ConfigurationProvider) plays a different role in the structural transformation during configuration, as to how they actually play a role, we will in what follows them for a special presentation.

二、Configuration

Configuration is always for the application in the form of a Configuration object we use, we are talking about is a corresponding Configuration object with all types IConfiguration implements all interfaces collectively. Meaning a Configuration object has a hierarchical tree structure does not mean that the corresponding data type has a member (field or property) corresponds to the definition, but that it provided in the API is logically reflect the hierarchical structure of the tree, so we He said configuration tree is a logical structure . Full definition is shown below IConfiguration interface, the so-called hierarchical logical structure embodied in the definition of its members.

   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 object represents a certain configuration node configuration tree. For all configurations the entire tree nodes, the root node representing the object representing Configuration Configuration object nodes other configurations are different, so the configuration of interfaces with different models to represent them. Specifically, the Configuration object root node is called ConfigurationRoot , in addition to the other Configuration object is called ConfigurationSection , respectively configuration model defines the interface IConfigurationRoot and IConfigurationSection to represent them, the two interfaces are IConfiguration Heir. The following figure shows us the configuration tree consists of a ConfigurationRoot ConfigurationSection object and a set of objects.

5

The interface is defined as follows IConfigurationRoot, showing only interface only way to achieve Reload reload configuration data. Configuration tree root ConfigurationRoot object represents, it may be simply a manifestation of whole grain configuration tree, if if it is reloaded, whole grain means that all configuration data carried by the configuration tree are reloaded.

   1: public interface IConfigurationRoot: IConfiguration
   2: {
   3:     void Reload();
   4: }

Represents the interface IConfigurationSection non-root node configuration has three attributes, read-only attribute is used to uniquely identify Key ConfigurationSection plurality of objects having the same parent node, said path Path and the current configuration of the node in the configuration tree, the path from the ConfigurationSection the Key composition, and the use of a colon ( ":") as a separator. Key combination Path and reflects the position of the current section disposed throughout the configuration tree.

   1: public interface IConfigurationSection: IConfiguration
   2: {    
   3:     string Path { get; }
   4:     string Key { get; }
   5:     string Value { get; set; }
   6: }

Value property represents IConfigurationSection configuration data for configuring the node bearer. In most cases, only the configuration tree leaf node corresponding to the object that has the value ConfigurationSection, non-leaf nodes corresponding to the object is merely ConfigurationSection represents a logical container to store all sub-nodes arranged, they generally returned Value Null. Should be one, the Value property is not read-only, but readable and writable, but the value we write in general are not persisted, so since the configuration tree is reloaded, the value written will be lost .

In the case of a basic understanding of ConfigurationRoot and ConfigurationSection we go back to the members defined in the interface of IConfiguration. All the self-configuration of nodes represents a collection ConfigurationSection GetChildren method returns it belongs to it, another method is obtained GetSection a specific sub-node configuration according to the specified Key. When GetSection method of execution, the parameters specified will be combined with the current ConfigurationSection the Path to determine the path of the target configuration node is located, so if specified when the method is called a relative to the current configuration section, we can get a sub-node configuration section below.

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["A:B:C"] = "ABC"
   4: };
   5: IConfiguration root = new ConfigurationBuilder()
   6:         .Add(new MemoryConfigurationSource { InitialData = 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 == "ABC");
  14: Debug.Assert(section2.Value == "ABC");
  15: Debug.Assert(section3.Value == "ABC");
  16:  
  17: Debug.Assert(!ReferenceEquals(section1, section2));
  18: Debug.Assert(!ReferenceEquals(section1, section3));        
  19: Debug.Assert(null != root.GetSection("D"));

As shown in the above code fragment, we call a method in a different way GetSection are obtained path " A: B: C " of ConfigurationSection. The code above also reflects another interesting phenomenon, although the three ConfigurationSection objects are pointing to the same node configuration tree, but they are not the same object . In other words, when we call GetSection method, regardless of whether there is a tree configuration matches the specified path configuration section, it always creates a ConfigurationSection object.

IConfiguration also has an index, we can specify the sub-Key configuration section or relative to the current path configuration of the node corresponding to the value obtained in ConfigurationSection. When the execution of this index, it will get a ConfigurationSection object according to methods consistent with the logic GetSection, and returns its Value property. If the configuration tree does not have to match the configuration section, the index will return Null without throwing an exception.

三、ConfigurationProvider

When first introduced ConfigurationSource object, we say that it reflects the original configuration source. Although the source of each different type of configuration has a corresponding type ConfigurationSource, but not for a read raw data provided by ConfigurationSource, but ConfigurationProvider delegate object corresponding to complete. In the arrangement described above, the conversion process, the source configured for different types of ConfigurationProvider achieved from an original configuration to a physical configuration of a structure in the manner as shown in FIG.

6

ConfigurationProvider is all achieved collectively for all types and the corresponding object IConfigurationProvider interface. Since the purpose is to convert the configuration ConfigurationProvider from the original structure into the physical structure, the physical structure of the configuration data to reflect a simple two-dimensional data dictionary, so we'll find methods defined in the interface IConfigurationProvider embodied mostly related operations for dictionary objects .

   1: public interface IConfigurationProvider
   2: {
   3:    void Load();
   4:  
   5:    bool TryGet(string key, out string value);
   6:    void Set(string key, string value);
   7:    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
   8: }

配置数据的加载通过调用ConfigurationProvider的Load方法来完成。我们可以调用TryGet方法获取由指定的Key所标识的配置项的值。从数据持久化的角度来讲,ConfigurationProvider基本上都是只读的,也就是说ConfigurationProvider只负责从持久化资源中读取配置数据,而不负责更新保存在持久化资源的配置数据,所以它提供的Set方法设置的配置数据一般只会保存在内存中。ConfigurationProvider的GetChildKeys方法用于获取某个指定配置节点的所有子节点的Key。

每种类型的配置源都具有对应的ConfigurationProvider类型,这些类型一般不会直接实现接口IConfigurationProvider,而会选择继承另一个名为ConfigurationProvider的抽象类。这个抽象类的定义其实很简单,从如下的代码片段可以看出它仅仅是对一个IDictionary<string, string>对象(Key不区分大小写)的封装,其Set和TryGetValue方法最终操作的都是这个字典对象。它实现了Load方法并将其定义成虚方法,具体的ConfigurationProvider可以通过重写这个方法从相应的数据源中读取配置数据并对这个字典对象进行初始化。

   1: public abstract class ConfigurationProvider : IConfigurationProvider
   2: {
   3:     protected IDictionary<string, string> Data { get; set; }
   4:  
   5:     public IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
   6:     {
   7:         //省略实现
   8:     }
   9:  
  10:     public virtual void Load()
  11:     {}
  12:  
  13:     public void Set(string key, string value)
  14:     {
  15:         this.Data[key] = value;
  16:     }
  17:  
  18:     public bool TryGet(string key, out string value)
  19:     {
  20:         return this.Data.TryGetValue(key, out value);
  21:     }
  22:     //其他成员
  23: }

四、ConfigurationSource

ConfiurationSource在配置模型中代表配置源,它通过注册到ConfigurationBuilder上为后者创建的Configuration提供原始的配置数据。由于针对原始配置数据的读取实现在相应的ConfigurationProvider之中,所以ConfigurationSource所起的作用在于提供相应的ConfigurationProvider。ConfigurationSource是对所有实现了IConfigurationSource接口的所有类型及其对象的统称,如下面的代码片段所示,该接口具有一个唯一的Build方法根据指定的ConfigurationBuilder对象提供对应的ConfigurationProvider。

   1: public interface IConfigurationSource
   2: {
   3:     IConfigurationProvider Build(IConfigurationBuilder builder);
   4: }

五、ConfigurationBuilder

ConfigurationBulder在整个配置模型中处于一个核心地位,它是Configuration的创建者,代表原始配置源的ConfigurationSource也注册到它上面。ConfigurationBulder是对所有实现了IConfigurationBulder接口的所有类型及其对应对象的统称。如下面的代码片段所示,IConfigurationBulder接口定义了两个方法,其中Add方法用于注册ConfigurationSource,最终的Configuration则通过Build方法创建,后者返回一个代表整棵配置的数的ConfigurationRoot对象。注册的ConfigurationSource被保存在通过Sources属性表示的集合中,而另一个属性Properties则以字典的形式存放任意的自定义属性。

   1: public interface IConfigurationBuilder
   2: {
   3:     IEnumerable<IConfigurationSource>  Sources { get; }
   4:     Dictionary<string, object>         Properties { get; }
   5:  
   6:     IConfigurationBuilder     Add(IConfigurationSource source);
   7:     IConfigurationRoot        Build();
   8: }

配置系统提供了一个名为ConfigurationBulder[1]的类作为IConfigurationBulder接口的默认实现者。定义在它上面的Build方法体现了配置系统读取原始配置数据并生成配置树的默认机制,这是我们接下来重点讲述的内容。ConfigurationBulder类的Build方法返回一个类型为ConfigurationRoot的对象,对于一个通过该对象表示配置树来说,每个非根配置节点均是一个类型为ConfigurationSection的对象,这两个类型(ConfigurationRoot和ConfigurationSection)自然是IConfigurationRoot和IConfigurationSection接口的实现者。

ConfigurationRoot代表着一颗完整的配置树,但是不论是这个对象本身,还是表示这棵树非根配置节点的ConfigurationSection对象,它们自身都没有维护任何的数据。这句话好似显得自相矛盾,但实则不然,因为所谓的配置树仅仅是API在逻辑上所体现的数据结构,并不是具体的配置数据也是按照这样的结构进行存储的。由于这两个对象均不作任何的数据封装,针对它们的数据提取请求最终都会交给一组ConfigurationProvider来完成,后者自然就是注册到ConfigurationBuilder上的这组ConfigurationSource所提供的ConfigurationProvider。

本节内容从设计和实现原理的角度对配置模型进行了详细的介绍。总的来说,配置模型涉及到四个核心对象,包括承载配置逻辑结构的Configuration对象和它的创建者ConfigurationBuilder,以及与配置源相关的ConfigurationSource和ConfigurationProvider。这四个核心对象之间的关系简单而清晰,完全可以通过一句话来概括:ConfigurationBuilder利用注册的ConfigurationSource来提供的ConfigurationProvider读取原始配置数据并创建出相应的Configuration对象。下图所示的UML展示了配置模型涉及的主要接口/类型以及它们之间的关系。

7


[1] 本小节提到的ConfigurationBuilder大部分情况下指代的是ConfigurationBuilder这个类型或者该类型的对象,而不是泛指所有实现了IConfigurationBulder接口的类型及其对应对象,。后面提到的ConfigurationRoot和ConfigurationSection也是这样,请读者朋友注意区分。

 

在《.NET Core采用的全新配置系统[1]: 读取配置数据》中,我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.NET Core的这个全新的配置系统具有一个大体上的认识,接下来我们从设计的维度来重写认识它。通过上面演示的实例我们知道,配置的编程模型涉及到三个核心对象,它们分别是ConfigurationConfigurationSourceConfigurationBuilder。如果从设计层面来审视这个配置系统,还缺少另一个名为ConfigurationProvider的核心对象,总得来说,.NET Core的这个配置模型由这四个核心对象组成。要彻底了解这四个核心对象之间的关系,我们先得来聊聊配置的几种数据结构。 [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、配置数据结构及其转换
二、Configuration
三、ConfigurationProvider
四、ConfigurationSource
五、ConfigurationBuilder

一、配置数据结构及其转换

相同的数据具有不同的表现和承载方式,同时体现出不同的数据结构。对于配置来说,它在被消费过程中是以Configuration对象的形式来体现的,该对象在逻辑上具有一个树形化层次结构,所以我们可以称之为配置树,并将这棵树视为配置的“逻辑结构”。

配置具有多种原始来源,可以是内存对象、物理文件、数据库或者其他自定义的存储介质,如果采用物理文件来存储配置数据,我们还可以选择不同的文件格式,常见的文件类型包括XML、JSON和INI三种,所以配置的原始数据结构是不确定的。配置模型的最终目的在于提取原始的配置数据并将其转换成一个Configuration对象,话句话说,整个配置模型的使命就在于按照下图所示的方式将配置数据从原始的结构转换成树形层次结构。

3

对于配置模型来说,配置从原始结构向逻辑结构的转换不是一蹴而就的,在它们之间具有一种“中间结构”。话句话说,原始的配置数据被读取出来之后会先统一转换成这种中间结构的数据,那么这种中间结构到底是一种怎样的数据结构呢?在《.NET Core采用的全新配置系统[1]: 读取配置数据》我们说过,一棵配置树通过其叶子结点承载所有的原子配置数据, 这棵树的结构和承载的数据完全可以利用一个简单的数据字典来表达。具体来说,我们只需要将所有叶子节点在配置树种的路径作为Key,将叶子结点承载的配置数据作为Value即可。所谓的“中间结构”指的就是这样的数据字典,我们不妨将其称为“物理结构”。所以配置模型会按照下图所示的方式将具有不同原始结构的配置数据统一转换成基于字典的物理结构,最终再完成针对逻辑结构的转换。

4

对于配置模型的四个核心对象,Configuration对配置树的体现,其他三个(ConfigurationSource、ConfigurationBuilder和ConfigurationProvider)在配置的结构转换过程中扮演着不同的角色,至于它们究竟起到怎样的作用,我们将在接下来的内容中对它们作专门的介绍。

二、Configuration

配置在应用程序中总是以一个Configuration对象的形式供我们使用,我们所说的Configuration是对所有实现了IConfiguration接口的所有类型一起对应对象的统称。一个Configuration对象具有树形层次化结构的意思并不是说对应的类型具有对应的数据成员(字段或者属性)定义,而是说它提供的API在逻辑上体现出树形化层次结构,所以我们才说配置树是一种逻辑结构。如下所示的是IConfiguration接口的完整定义,所谓的层次化逻辑结构就体现在它的成员定义上。

   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对象被称为ConfigurationRoot,除此之外的其他Configuration对象则被称为ConfigurationSection,配置模型分别定义了接口IConfigurationRootIConfigurationSection来表示它们,这两个接口都是IConfiguration的继承者。下图为我们展示了由一个ConfigurationRoot对象和一组 ConfigurationSection对象构成的配置树。

5

如下所示的是接口IConfigurationRoot的定义,可见该接口仅仅唯一的方法Reload实现对配置数据的重新加载。ConfigurationRoot对象表示的配置树的根,也可以是它根本就是对整棵配置树的体现,如果如果它被重新加载了,意味着整棵配置树承载的所有配置数据均被重新加载了。

   1: public interface IConfigurationRoot : IConfiguration
   2: {
   3:     void Reload();
   4: }

表示非根配置节点的IConfigurationSection接口具有如下三个属性,只读属性Key用来唯一标识多个具有相同父节点的ConfigurationSection对象,而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得到一个具体的子配置节点。当GetSection方法执行的时候,指定的参数将会与当前ConfigurationSection的Path进行组合以确定目标配置节点所在的路径,所以如果在调用该方法的时候指定一个相对于当前配置节的路径,我们是可以得到子节点以下的某个配置节。

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["A:B:C"] = "ABC"
   4: };
   5: IConfiguration root = new ConfigurationBuilder()
   6:         .Add(new MemoryConfigurationSource { InitialData = 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 == "ABC");
  14: Debug.Assert(section2.Value == "ABC");
  15: Debug.Assert(section3.Value == "ABC");
  16:  
  17: Debug.Assert(!ReferenceEquals(section1, section2));
  18: Debug.Assert(!ReferenceEquals(section1, section3));        
  19: Debug.Assert(null != root.GetSection("D"));

如上面的代码片段所示,我们以不同的方式调用GetSection方法得到的都是路径为“A:B:C”的ConfigurationSection。上面这段代码还体现了另一个有趣的现象,虽然这三个ConfigurationSection对象均指向配置树的同一个节点,但是它们却并非同一个对象。换句话说,当我们调用GetSection方法的时候,不论配置树种是否存在一个与指定路径匹配的配置节,它总是会创建一个ConfigurationSection对象。

IConfiguration还具有一个索引,我们可以指定子配置节的Key或者相对当前配置节点的路径得到对应ConfigurationSection的值。当这个索引执行的时候,它会按照与GetSection方法完全一致的逻辑得到一个ConfigurationSection对象,并返回其Value属性。如果配置树中不具有匹配的配置节,该索引会返回Null而不会抛出异常。

三、ConfigurationProvider

在第一节介绍ConfigurationSource对象时,我们说它对原始配置源的体现。虽然每种不同类型的配置源都具有一个对应的ConfigurationSource类型,但是针对原始数据的读取并不由ConfigurationSource来提供,而是委托一个对应的ConfigurationProvider对象来完成。在上面介绍的配置结构转换过程中,针对不同配置源类型的ConfigurationProvider按照如下图所示的方式实现配置从原始结构向物理结构的转换。

6

ConfigurationProvider是对所有实现了IConfigurationProvider接口的所有类型以及对应对象的统称。由于ConfigurationProvider的目的在于将配置从原始结构转换成物理结构,配置数据的物理结构体现为一个简单的二维数据字典,所以我们会发现定义在IConfigurationProvider接口中的方法大都体现为针对字典对象的相关操作。

   1: public interface IConfigurationProvider
   2: {
   3:    void Load();
   4:  
   5:    bool TryGet(string key, out string value);
   6:    void Set(string key, string value);
   7:    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
   8: }

配置数据的加载通过调用ConfigurationProvider的Load方法来完成。我们可以调用TryGet方法获取由指定的Key所标识的配置项的值。从数据持久化的角度来讲,ConfigurationProvider基本上都是只读的,也就是说ConfigurationProvider只负责从持久化资源中读取配置数据,而不负责更新保存在持久化资源的配置数据,所以它提供的Set方法设置的配置数据一般只会保存在内存中。ConfigurationProvider的GetChildKeys方法用于获取某个指定配置节点的所有子节点的Key。

每种类型的配置源都具有对应的ConfigurationProvider类型,这些类型一般不会直接实现接口IConfigurationProvider,而会选择继承另一个名为ConfigurationProvider的抽象类。这个抽象类的定义其实很简单,从如下的代码片段可以看出它仅仅是对一个IDictionary<string, string>对象(Key不区分大小写)的封装,其Set和TryGetValue方法最终操作的都是这个字典对象。它实现了Load方法并将其定义成虚方法,具体的ConfigurationProvider可以通过重写这个方法从相应的数据源中读取配置数据并对这个字典对象进行初始化。

   1: public abstract class ConfigurationProvider : IConfigurationProvider
   2: {
   3:     protected IDictionary<string, string> Data { get; set; }
   4:  
   5:     public IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
   6:     {
   7:         //省略实现
   8:     }
   9:  
  10:     public virtual void Load()
  11:     {}
  12:  
  13:     public void Set(string key, string value)
  14:     {
  15:         this.Data[key] = value;
  16:     }
  17:  
  18:     public bool TryGet(string key, out string value)
  19:     {
  20:         return this.Data.TryGetValue(key, out value);
  21:     }
  22:     //其他成员
  23: }

四、ConfigurationSource

ConfiurationSource在配置模型中代表配置源,它通过注册到ConfigurationBuilder上为后者创建的Configuration提供原始的配置数据。由于针对原始配置数据的读取实现在相应的ConfigurationProvider之中,所以ConfigurationSource所起的作用在于提供相应的ConfigurationProvider。ConfigurationSource是对所有实现了IConfigurationSource接口的所有类型及其对象的统称,如下面的代码片段所示,该接口具有一个唯一的Build方法根据指定的ConfigurationBuilder对象提供对应的ConfigurationProvider。

   1: public interface IConfigurationSource
   2: {
   3:     IConfigurationProvider Build(IConfigurationBuilder builder);
   4: }

五、ConfigurationBuilder

ConfigurationBulder在整个配置模型中处于一个核心地位,它是Configuration的创建者,代表原始配置源的ConfigurationSource也注册到它上面。ConfigurationBulder是对所有实现了IConfigurationBulder接口的所有类型及其对应对象的统称。如下面的代码片段所示,IConfigurationBulder接口定义了两个方法,其中Add方法用于注册ConfigurationSource,最终的Configuration则通过Build方法创建,后者返回一个代表整棵配置的数的ConfigurationRoot对象。注册的ConfigurationSource被保存在通过Sources属性表示的集合中,而另一个属性Properties则以字典的形式存放任意的自定义属性。

   1: public interface IConfigurationBuilder
   2: {
   3:     IEnumerable<IConfigurationSource>  Sources { get; }
   4:     Dictionary<string, object>         Properties { get; }
   5:  
   6:     IConfigurationBuilder     Add(IConfigurationSource source);
   7:     IConfigurationRoot        Build();
   8: }

配置系统提供了一个名为ConfigurationBulder[1]的类作为IConfigurationBulder接口的默认实现者。定义在它上面的Build方法体现了配置系统读取原始配置数据并生成配置树的默认机制,这是我们接下来重点讲述的内容。ConfigurationBulder类的Build方法返回一个类型为ConfigurationRoot的对象,对于一个通过该对象表示配置树来说,每个非根配置节点均是一个类型为ConfigurationSection的对象,这两个类型(ConfigurationRoot和ConfigurationSection)自然是IConfigurationRoot和IConfigurationSection接口的实现者。

ConfigurationRoot代表着一颗完整的配置树,但是不论是这个对象本身,还是表示这棵树非根配置节点的ConfigurationSection对象,它们自身都没有维护任何的数据。这句话好似显得自相矛盾,但实则不然,因为所谓的配置树仅仅是API在逻辑上所体现的数据结构,并不是具体的配置数据也是按照这样的结构进行存储的。由于这两个对象均不作任何的数据封装,针对它们的数据提取请求最终都会交给一组ConfigurationProvider来完成,后者自然就是注册到ConfigurationBuilder上的这组ConfigurationSource所提供的ConfigurationProvider。

This section from the perspective of design and realization of the principle of the configuration model is described in detail. In general, the configuration model involves four core objects, comprising a carrier structure configuration logic Configuration object and its creator ConfigurationBuilder, and information related to the configuration of the source and ConfigurationSource ConfigurationProvider. Simple relationship between these four core objects and clear, can be summed up in one sentence: ConfigurationBuilder use registered ConfigurationSource to provide ConfigurationProvider read the original configuration data and create the corresponding Configuration object. UML shown below shows the relationship between the main interface / models according to the type and configuration therebetween.

7


[1]  In most cases ConfigurationBuilder referred to in this section refer to the type of object or the type ConfigurationBuilder instead refers to all types IConfigurationBulder implements the interface and the corresponding object. The latter mentioned ConfigurationRoot and ConfigurationSection too, please readers attention distinction.

 

Guess you like

Origin www.cnblogs.com/webenh/p/11589886.html