The new configuration of the system used in .NET Core [1]: configuration data is read

Refer to "Configuration" word, I think the vast majority of .NET developers mind will immediately emerge out of the shadow of two special files that we are familiar of app.config and web.config, we have become accustomed to over the years the structure of the configuration definitions in these two documents. To the .NET Core era, many of us take for granted things have changed, including the way defined configuration. Overall, the new configuration system even more lightweight, and has better scalability, its biggest feature is support for a variety of data sources. We can use the memory of the configuration variable as a data source, you can also configure the definition in the persistence of files and even databases. Because many people do not have contact with this configuration system with a new design, in order to let everyone have a general understanding of it, we start with a programming perspective to experience a new way to read the configuration. This new configuration system to read configuration defines a very simple API, which involves three core objects, we might call " configuration programming model of the three elements ." [This article has been synchronized to the " ASP.NET Framework Core Secret " among]

Contents
First, the programming model of the three elements disposed
Second, the key - value pairs arranged to read
three structured configuration read
four structured configured to bind directly to objects

First, the configuration of the three elements of the programming model

To level programming terms, .NET Core configuration of the system consists of three core object as shown below. Read out the configuration information will eventually be converted into a Configuration objects for our program. ConfigurationBuilder is the Configuration object builders, and ConfigurationSource represents the most primitive source configuration.

1

In reading the configuration, we create a corresponding ConfigurationSource definition mode configuration, and register it to the ConfigurationBuilder objects created. Due to the configuration of the original source may provide more than one, so we can register multiple identical or different types of ConfigurationSource objects onto ConfigurationBuilder. This is the raw data ConfigurationBuilder use of these registered ConfigurationSource provide final build Configuration objects we use in the program.

According to the naming of this series has always been used, we should know Configuration, ConfigurationSource and ConfigurationBuilder described above are collectively referred to a class of objects, which are in the API level, through the appropriate interface (IConfiguration, IConfigurationSource and IConfigurationBuilder) to represent these NuGet package interfaces are defined in the "Mic rosoft.Extensions.Configuration.Abstractions " in. If our program only need to use these interfaces, we just need to add a dependency for this NuGet package. As these default implementation of the interface type, it is mostly defined in the " Microsoft.Extensions.Configuration " NuGet this package.

Second, the bond - to read configuration value pairs

Although the configuration in most cases from the overall structure of the relationship have time, but the "atomic" configuration items are the most simple "key - value" to reflect the form and character keys and values ​​are usually string, then we will be through a simple example to demonstrate how the form of key-value pairs to read the configuration. We create a console application for ASP.NET Core, and add for the dependent "Microsoft.Extensions.Configuration" NuGet this package, configuration model to achieve in this package in the following manner project.json in.

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Configuration": "1.0.0 "
   5:   },
   6: }

Suppose we need to configure the application to set the display format of the date / time, and we will be relevant configuration information defined in the class DateTimeFormatOptions shown below, which reflect the properties of the display format of four for the four DateTime object (long respectively date / time is short and date / time).

   1: public class DateTimeFormatOptions
   2: {
   3:     public string LongDatePattern { get; set; }
   4:     public string LongTimePattern { get; set; }
   5:     public string ShortDatePattern { get; set; }
   6:     public string ShortTimePattern { get; set; }
   7: // other members
   8: }

We want to control the date embodied by the form of the four properties DateTimeFormatOptions configuration / time display format, so we define a constructor for it. The following code fragment, this configuration has a function IConfiguration interface type parameters, Through the above description we know that it is embodied in the application configuration. Key-value pair is the basic form of the configuration, so the Configuration object provides an index so that we can get the value of configuration items according to Key configuration items, the following code calls the official way to get the corresponding index configuration information.

   1: public class DateTimeFormatOptions
   2: {
   3: // other members
   4:     public DateTimeFormatOptions (IConfiguration config)
   5:     {
   6:         this.LongDatePattern     = config ["LongDatePattern"];
   7:         this.LongTimePattern     = config ["LongTimePattern"];
   8:         this.ShortDatePattern    = config ["ShortDatePattern"];
   9:         this.ShortTimePattern    = config ["ShortTimePattern"];
  10:     }
  11: }

To create a DateTimeFormatOptions reflect the current configuration of the object, we have to get this Configuration object carrying the relevant configuration information. As we mentioned above, Configuration object is created by the ConfigurationBuilder, and the original configuration information is through the corresponding ConfigurationSource to extract, so the correct programmatically create a Configuration object is to create a ConfigurationBuilder object, and then a registration whom ConfigurationSource or more objects, and finally we need to create a Configuration object using ConfigurationBuilder.

We arranged to read and convert it into a target DateTimeFormatOptions by the following procedure. For simplicity, we use a type of MemoryConfigurationSource of ConfigurationSource, it is the direct use of a dictionary object stored in memory as the source of the initial configuration. The following code fragment, we set four types of date dictionary object MemoryConfigurationSource provided / time display format.

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["longDatePattern"] = "dddd, MMMM d, yyyy",
   4:     ["longTimePattern"] = "h:mm:ss tt",
   5:     ["shortDatePattern"] = "M/d/yyyy",
   6:     ["shortTimePattern"] = "h:mm tt"
   7: };
   8:  
   9: IConfiguration config = new ConfigurationBuilder()
  10:     .Add(new MemoryConfigurationSource { InitialData = source })
  11:     .Build();
  12:  
  13: DateTimeFormatOptions options = new DateTimeFormatOptions(config);
  14: Console.WriteLine($"LongDatePattern: {options.LongDatePattern}");
  15: Console.WriteLine($"LongTimePattern: {options.LongTimePattern}");
  16: Console.WriteLine($"ShortDatePattern: {options.ShortDatePattern}");
  17: Console.WriteLine($"ShortTimePattern: {options.ShortTimePattern}");

We have created a ConfigurationBuilder type of objects, and register this MemoryConfigurationSource to it. Next, we call the direct method Build ConfigurationBuilder create a Configuration object, and use the latter creates a DateTimeFormatOptions object. In order to verify DateTimeFormatOptions object is consistent with the original configuration, we will print its four properties on the console. After running, the results shown below will produce an output on the console.

   1: LongDatePattern : dddd, MMMM d, yyyy
   2: LongTimePattern : h:mm:ss tt
   3: ShortDatePattern: M/d/yyyy
   4: ShortTimePattern: h:mm tt

Third, the configuration of the read structure

Configuring real project involved most of hierarchical structured so Configuration object also has such a structure. Structured configuration has a hierarchical tree structure, we might call it "tree configuration", a final Configuration object corresponds to a node in the tree configuration tree, and whole grain configuration may correspond to a natural tree root node Configuration objects to represent. In embodying key "atomic configuration item" generally corresponds to the "leaf node" is not in the configuration tree has a child node.

Next we have the same way of example to demonstrate how to define and read the configuration has a hierarchy. We are still in use on an application scenario, but now we just need to set the format of the date / time, other data also need to set the type of format, such as Decimal represent the type of currency. To this end we define as a CurrencyDecimalFormatOptions class, its properties and Symbol Digits represent the number of decimal places and currency symbol, a CurrencyDecimalFormatOptions the object still is the use of a Configuration object to create.

   1: public class CurrencyDecimalFormatOptions
   2: {
   3:     public int        Digits { get; set; }
   4:     public string     Symbol { get; set; }
   5:  
   6:     public CurrencyDecimalFormatOptions (IConfiguration config)
   7:     {
   8:         this.Digits = int.Parse(config["Digits"]);
   9:         this.Symbol = config["Symbol"];
  10:     }
  11: }

We define a type called FormatOptions to represent another set of data for different types of formats. As shown in the following code fragment, and its two attributes DateTime CurrencyDecimal respectively provided for the date / time and currency digital format. FormatOptions constructor still has a parameter of type IConfiguration interface, two of its properties are initialized in this constructor. It is noteworthy that these two properties used to initialize the current Configuration "child configuration section," we get these two sub-configuration sections GetSection method call by specifying the name of the configuration section.

   1: public class FormatOptions
   2: {
   3:     public DateTimeFormatOptions            DateTime { get; set; }
   4:     public CurrencyDecimalFormatOptions     CurrencyDecimal { get; set; }
   5:  
   6:     public FormatOptions (IConfiguration config)
   7:     {
   8:         this.DateTime = new DateTimeFormatOptions (config.GetSection("DateTime"));
   9:         this.CurrencyDecimal = new CurrencyDecimalFormatOptions (config.GetSection("CurrencyDecimal"));
  10:     }
  11: }

FormatOptions type configuration having a hierarchical tree embodied structure shown below. In our example above demonstrates, we MemoryConfigurationSource by a primitive object to provide configuration information. Since the original bearer configuration information element is a type KeyValuePair <string, string> collection, it does not have a tree in the hierarchy of the physical storage, then how it can ultimately provide a structured Configuration object?

2

The solution is very simple, for a complete configuration tree, the specific configuration information is ultimately carried by a leaf node, so MemoryConfigurationSource only need to save the data to a leaf node in the configuration dictionary. In addition, to describe the structure of the configuration tree, the dictionary needs to configure the path corresponding to the configuration tree leaf node as the Key. As delimiters: MemoryConfigurationSource arranged so dictionary number shown in the table of configuration "flat" may be employed, using the path colon ( "").

 

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

$

 

The following code fragment, we create the structure shown in Table 1 according to a Dictionary <string, string> object and use it to create MemoryConfigurationSource object. After obtaining the Configuration object represents the entire configuration using ConfigurationBuildr, we call its methods to get GetSection configuration section named "Format", and use the latter to create a FormatOptions.

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["format:dateTime:longDatePattern"] = "dddd, MMMM d, yyyy",
   4:     ["format:dateTime:longTimePattern"] = "h:mm:ss tt",
   5:     ["format:dateTime:shortDatePattern"] = "M/d/yyyy",
   6:     ["format:dateTime:shortTimePattern"] = "h:mm tt",
   7:  
   8:     ["format:currencyDecimal:digits"] = "2",
   9:     ["format:currencyDecimal:symbol"] = "$",
  10: };
  11: IConfiguration configuration = new ConfigurationBuilder()
  12:         .Add(new MemoryConfigurationSource { InitialData = source })
  13:         .Build();
  14:  
  15: FormatOptions options = new FormatOptions(configuration.GetSection("Format"));
  16: DateTimeFormatOptions dateTime = options.DateTime;
  17: CurrencyDecimalFormatOptions currencyDecimal = options.CurrencyDecimal;
  18:  
  19: Console.WriteLine("DateTime:");
  20: Console.WriteLine($"\tLongDatePattern: {dateTime.LongDatePattern}");
  21: Console.WriteLine($"\tLongTimePattern: {dateTime.LongTimePattern}");
  22: Console.WriteLine($"\tShortDatePattern: {dateTime.ShortDatePattern}");
  23: Console.WriteLine($"\tShortTimePattern: {dateTime.ShortTimePattern}");        
  24:  
  25: Console.WriteLine("CurrencyDecimal:");
  26: Console.WriteLine($"\tDigits:{currencyDecimal.Digits}");
  27: Console.WriteLine($"\tSymbol:{currencyDecimal.Symbol}");

After obtaining the configuration objects created FormatOptions use to read, in order to verify that the object is consistent with the original configuration data, we will still print its related properties on the console. After this procedure the program will change after presentation of output results are shown on the console. (S02)

   1: DateTime:
   2:         LongDatePattern : dddd, MMMM d, yyyy
   3:         LongTimePattern : h:mm:ss tt
   4:         ShortDatePattern: M/d/yyyy
   5:         ShortTimePattern: h:mm tt
   6:  
   7: CurrencyDecimal:
   8:         Digits          : 2
   9:         Symbol          : $

Fourth, the structural configuration bind directly to objects

In the real project development process, we tend to like the two examples we demonstrate as to define a set of related configuration options (Option) by creating the appropriate type (such as demo instance DateTimeFormatOptions, CurrencyDecimalOptions and FormatOptions), we will define configuration options (option) of these types are known as option type. In the example illustrated above, in order to create these objects package configurations, we are all manually read the configuration of the form, if you define the configuration items too much, one by one to read the configuration item is actually a very tedious work.

对于一个对象来说,如果我们将它的属性视为它的子节点,一个对象同样具有类似于Configuration对象的树形层次结构。如果我们根据某个Option类型的结构来定义配置,或者根据配置的结构来定义这个Option类型,Option类型的属性成员将与某个配置节具有一一对应的关系,那么原则上我们可以自动将配置信息绑定为一个具体的Option对象。

.NET Core的配置系统采用一种叫做“Options Pattern”的编程模式来支持从原始配置到Options对象之间的绑定。这种编程模式涉及的API定义在“Microsoft.Extensions.Options.ConfigurationExtensions”这个NuGet包中,所以我们需要在project.json文件中按照如下的方式添加针对性的依赖。除此之外,“Options Pattern”涉及到对DI的使用,所以我们还需要添加针对NuGet包“Microsoft.Extensions
.DependencyInjection”的依赖。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Options.ConfigurationExtensions"    : "1.0.0",
   5:     "Microsoft.Extensions.DependencyInjection"                : "1.0.0"
   6:   },
   7: }

借助于Options Pattern的自动绑定机制,我们无需逐条地读取配置,所以我们可以将这个三个Options类型(DateTimeFormatOptions、CurrencyDecimalOptions和FormatOptions)的构造函数全部删除,只保留其属性成员。在作为程序入口的Main方法中,我们采用如下的方式创建这个表示格式设置的FormatOptions对象。

   1: ...
   2: FormatOptions options = new ServiceCollection()
   3:     .AddOptions()
   4:     .Configure<FormatOptions>(config.GetSection("Format"))
   5:     .BuildServiceProvider()
   6:     .GetService<IOptions<FormatOptions>>()
   7:     .Value;

如上面的代码片段所示,我们创建一个ServiceCollection对象并调用扩展方法AddOptions注册于针对Option模型的服务。接下来我们调用Configure方法将FormatOptions这个Option类型与对应的Configuration对象进行映射。我们最后利用这个ServiceCollection对象生成一个ServiceProvider,并调用其GetService方法得到一个类型为IOptions<FormatOptions>的对象,后者的Value属性返回的就是绑定了相关配置的FormatOptions对象。

 

提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个文件之中。到了.NET Core的时代,很多我们习以为常的东西都发生了改变,其中也包括定义配置的方式。总的来说,新的配置系统显得更加轻量级,并且具有更好的扩展性,其最大的特点就是支持多样化的数据源。我们可以采用内存的变量作为配置的数据源,也可以直接配置定义在持久化的文件甚至数据库中。由于很多人都不曾接触过这个采用全新设计的配置系统,为了让大家对它有一个大体的认识,我们先从编程的角度来体验一下全新的配置读取方式。这个全新的配置系统为配置的读取定义了非常简单的API,这些API涉及到三个核心的对象,我们不妨称之为“配置编程模型三要素”。 [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、配置编程模型三要素
二、以键-值对的形式读取配置
三、读取结构化的配置
四、将结构化配置直接绑定为对象

一、配置编程模型三要素

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

1

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

根据本系列文章一贯采用的命名方式,我们应该知道上面介绍的Configuration、ConfigurationSource和ConfigurationBuilder均是对一类对象的统称,它们在API层面都通过相应的接口(IConfiguration、IConfigurationSource和IConfigurationBuilder)来表示,这些接口均义在NuGet包“Microsoft.Extensions.Configuration.Abstractions”中。如果我们的程序中只需要使用到这些接口,我们只需要添加针对这个NuGet包的依赖。至于这些接口的默认实现类型,则大多定义在“Microsoft.Extensions.Configuration”这个NuGet包中。

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

虽然在大部分情况下的配置从整体来说都具有结构化的次关系,但是“原子”配置项都以最简单的“键-值对”的形式来体现,并且键和值通常都是字符串,接下来我们会通过一个简单的实例来演示如何以键值对的形式来读取配置。我们创建一个针对ASP.NET Core的控制台应用,并在project.json中按照如下的方式添加针对“Microsoft.Extensions.Configuration”这个NuGet包的依赖,配置模型就实现在这个包中。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Configuration": "1.0.0 "
   5:   },
   6: }

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

   1: public class DateTimeFormatOptions
   2: {
   3:     public string LongDatePattern { get; set; }
   4:     public string LongTimePattern { get; set; }
   5:     public string ShortDatePattern { get; set; }
   6:     public string ShortTimePattern { get; set; }
   7:     //其他成员
   8: }

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

   1: public class DateTimeFormatOptions
   2: {
   3:     //其他成员
   4:     public DateTimeFormatOptions (IConfiguration config)
   5:     {
   6:         this.LongDatePattern     = config ["LongDatePattern"];
   7:         this.LongTimePattern     = config ["LongTimePattern"];
   8:         this.ShortDatePattern    = config ["ShortDatePattern"];
   9:         this.ShortTimePattern    = config ["ShortTimePattern"];
  10:     }
  11: }

要创建一个体现当前配置的DateTimeFormatOptions对象,我们必须向得到这个承载相关配置信息的Configuration对象。正如我们上面所说,Configuration对象是由ConfigurationBuilder创建的,而原始的配置信息则是通过相应的ConfigurationSource来提取的,所以创建一个Configuration对象的正确编程方式是先创建一个ConfigurationBuilder对象,然后为之注册一个或者多个ConfigurationSource对象,最后利用ConfigurationBuilder来创建我们需要的Configuration对象。

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

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["longDatePattern"] = "dddd, MMMM d, yyyy",
   4:     ["longTimePattern"] = "h:mm:ss tt",
   5:     ["shortDatePattern"] = "M/d/yyyy",
   6:     ["shortTimePattern"] = "h:mm tt"
   7: };
   8:  
   9: IConfiguration config = new ConfigurationBuilder()
  10:     .Add(new MemoryConfigurationSource { InitialData = source })
  11:     .Build();
  12:  
  13: DateTimeFormatOptions options = new DateTimeFormatOptions(config);
  14: Console.WriteLine($"LongDatePattern: {options.LongDatePattern}");
  15: Console.WriteLine($"LongTimePattern: {options.LongTimePattern}");
  16: Console.WriteLine($"ShortDatePattern: {options.ShortDatePattern}");
  17: Console.WriteLine($"ShortTimePattern: {options.ShortTimePattern}");

我们创建了一个ConfigurationBuilder类型的对象,并将这个MemoryConfigurationSource注册到它上面。接下来,我们直接调用ConfigurationBuilder的Build方法创建出Configuration对象,并利用后者创建了一个DateTimeFormatOptions对象。为了验证DateTimeFormatOptions对象是否与原始的配置一致,我们将它的四个属性打印在控制台上。程序运行之后,控制台上将会产生如下所示的输出结果。

   1: LongDatePattern : dddd, MMMM d, yyyy
   2: LongTimePattern : h:mm:ss tt
   3: ShortDatePattern: M/d/yyyy
   4: ShortTimePattern: h:mm tt

三、读取结构化的配置

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

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

   1: public class CurrencyDecimalFormatOptions
   2: {
   3:     public int        Digits { get; set; }
   4:     public string     Symbol { get; set; }
   5:  
   6:     public CurrencyDecimalFormatOptions (IConfiguration config)
   7:     {
   8:         this.Digits = int.Parse(config["Digits"]);
   9:         this.Symbol = config["Symbol"];
  10:     }
  11: }

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

   1: public class FormatOptions
   2: {
   3:     public DateTimeFormatOptions            DateTime { get; set; }
   4:     public CurrencyDecimalFormatOptions     CurrencyDecimal { get; set; }
   5:  
   6:     public FormatOptions (IConfiguration config)
   7:     {
   8:         this.DateTime = new DateTimeFormatOptions (config.GetSection("DateTime"));
   9:         this.CurrencyDecimal = new CurrencyDecimalFormatOptions (config.GetSection("CurrencyDecimal"));
  10:     }
  11: }

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

2

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

 

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

$

 

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

   1: Dictionary<string, string> source = new Dictionary<string, string>
   2: {
   3:     ["format:dateTime:longDatePattern"] = "dddd, MMMM d, yyyy",
   4:     ["format:dateTime:longTimePattern"] = "h:mm:ss tt",
   5:     ["format:dateTime:shortDatePattern"] = "M/d/yyyy",
   6:     ["format:dateTime:shortTimePattern"] = "h:mm tt",
   7:  
   8:     ["format:currencyDecimal:digits"] = "2",
   9:     ["format:currencyDecimal:symbol"] = "$",
  10: };
  11: IConfiguration configuration = new ConfigurationBuilder()
  12:         .Add(new MemoryConfigurationSource { InitialData = source })
  13:         .Build();
  14:  
  15: FormatOptions options = new FormatOptions(configuration.GetSection("Format"));
  16: DateTimeFormatOptions dateTime = options.DateTime;
  17: CurrencyDecimalFormatOptions currencyDecimal = options.CurrencyDecimal;
  18:  
  19: Console.WriteLine("DateTime:");
  20: Console.WriteLine($"\tLongDatePattern: {dateTime.LongDatePattern}");
  21: Console.WriteLine($"\tLongTimePattern: {dateTime.LongTimePattern}");
  22: Console.WriteLine($"\tShortDatePattern: {dateTime.ShortDatePattern}");
  23: Console.WriteLine($"\tShortTimePattern: {dateTime.ShortTimePattern}");        
  24:  
  25: Console.WriteLine("CurrencyDecimal:");
  26: Console.WriteLine($"\tDigits:{currencyDecimal.Digits}");
  27: Console.WriteLine($"\tSymbol:{currencyDecimal.Symbol}");

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

   1: DateTime:
   2:         LongDatePattern : dddd, MMMM d, yyyy
   3:         LongTimePattern : h:mm:ss tt
   4:         ShortDatePattern: M/d/yyyy
   5:         ShortTimePattern: h:mm tt
   6:  
   7: CurrencyDecimal:
   8:         Digits          : 2
   9:         Symbol          : $

四、将结构化配置直接绑定为对象

在真正的项目开发过程中,我们倾向于像我们演示的两个实例一样通过创建相应的类型(比如演示实例中的DateTimeFormatOptions、CurrencyDecimalOptions和FormatOptions)来定义一组相关的配置选项(Option),我们将定义配置选项(Option)的这些类型称为Option类型。在上面演示的实例中,为了创建这些封装配置的对象,我们都是采用手工读取配置的形式,如果定义的配置项太多的话,逐条读取配置项其实是一项非常繁琐的工作。

对于一个对象来说,如果我们将它的属性视为它的子节点,一个对象同样具有类似于Configuration对象的树形层次结构。如果我们根据某个Option类型的结构来定义配置,或者根据配置的结构来定义这个Option类型,Option类型的属性成员将与某个配置节具有一一对应的关系,那么原则上我们可以自动将配置信息绑定为一个具体的Option对象。

.NET Core的配置系统采用一种叫做“Options Pattern”的编程模式来支持从原始配置到Options对象之间的绑定。这种编程模式涉及的API定义在“Microsoft.Extensions.Options.ConfigurationExtensions”这个NuGet包中,所以我们需要在project.json文件中按照如下的方式添加针对性的依赖。除此之外,“Options Pattern”涉及到对DI的使用,所以我们还需要添加针对NuGet包“Microsoft.Extensions
.DependencyInjection”的依赖。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Options.ConfigurationExtensions"    : "1.0.0",
   5:     "Microsoft.Extensions.DependencyInjection"                : "1.0.0"
   6:   },
   7: }

Options Pattern by means of automatic binding mechanism, we do not need one by one to read the configuration, so we can delete all the three types Options (DateTimeFormatOptions, CurrencyDecimalOptions and FormatOptions) constructor, leaving only the members of its properties. In the Main method as a program entry, we use the following way to create FormatOptions objects that represent formatting.

   1: ...
   2: FormatOptions options = new ServiceCollection()
   3:     .AddOptions()
   4:     .Configure<FormatOptions>(config.GetSection("Format"))
   5:     .BuildServiceProvider()
   6:     .GetService<IOptions<FormatOptions>>()
   7:     .Value;

As shown in the above code snippet, we create a ServiceCollection object and call the extension method for Option AddOptions registered service model. Next we call the Configure method FormatOptions this Option Configuration object type and the corresponding map. Finally, we use this ServiceCollection a ServiceProvider object generation, and call its methods to obtain a type GetService IOptions <FormatOptions> object, the latter is returned Value property bound formatoptions objects associated configuration.

 

Guess you like

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