core2.1读取配置数据
一、以键-值对的形式读取配置
虽然在大部分情况下的配置从整体来说都具有结构化的次关系,但是“原子”配置项都以最简单的“键-值对”的形式来体现,并且键和值通常都是字符串,接下来我们会通过一个简单的实例来演示如何以键值对的形式来读取配置。我们创建一个针对ASP.NET Core的控制台应用,并在csproj中按照如下的方式添加针对“Microsoft.Extensions.Configuration”这个NuGet包的依赖,配置模型就实现在这个包中。
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
假设我们的应用程序需要通过配置来设定日期/时间的显示格式,为此我们将相关的配置信息定义在如下所示的这个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接口类型的参数,通过上面的介绍我们知道它是配置在应用程序中体现。键值对是配置的基本表现形式,所以Configuration对象提供了索引使我们可以根据配置项的Key得到配置项的值,下面的代码正式调用索引的方式得到对应配置信息的。
public class DateTimeFormatOptions { //其他成员 public DateTimeFormatOptions (IConfiguration config) { this.LongDatePattern = config ["LongDatePattern"]; this.LongTimePattern = config ["LongTimePattern"]; this.ShortDatePattern = config ["ShortDatePattern"]; this.ShortTimePattern = config ["ShortTimePattern"]; } }
要创建一个体现当前配置的DateTimeFormatOptions对象,我们必须向得到这个承载相关配置信息的Configuration对象。正如我们上面所说,Configuration对象是由ConfigurationBuilder创建的,而原始的配置信息则是通过相应的ConfigurationSource来提取的,所以创建一个Configuration对象的正确编程方式是先创建一个ConfigurationBuilder对象,然后为之注册一个或者多个ConfigurationSource对象,最后利用ConfigurationBuilder来创建我们需要的Configuration对象。
我们通过如下的程序来读取配置并将其转换成一个DateTimeFormatOptions对象。简单起见,我们采用一中类型为MemoryConfigurationSource的ConfigurationSource,它直接利用一个保存在内存中的字典对象作为最初的配置来源。如下面的代码片段所示,我们在为MemoryConfigurationSource提供的字典对象中设置了四种类型的日期/时间显示格式。
Dictionary<string, string> source = new Dictionary<string, string> { ["longDatePattern"] = "dddd, MMMM d, yyyy", ["longTimePattern"] = "h:mm:ss tt", ["shortDatePattern"] = "M/d/yyyy", ["shortTimePattern"] = "h:mm tt" }; IConfiguration config = new ConfigurationBuilder() .Add(new MemoryConfigurationSource { InitialData = source }) .Build(); DateTimeFormatOptions 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方法创建出Configuration对象,并利用后者创建了一个DateTimeFormatOptions对象。为了验证DateTimeFormatOptions对象是否与原始的配置一致,我们将它的四个属性打印在控制台上。程序运行之后,控制台上将会产生如下所示的输出结果。
LongDatePattern : dddd, MMMM d, yyyy LongTimePattern : h:mm:ss tt ShortDatePattern: M/d/yyyy ShortTimePattern: h:mm tt
二、读取结构化的配置
真实项目中涉及的配置大都具有结构化的层次结构,所以Configuration对象同样具有这样的结构。结构化配置具有一个树形层次结构,我们不妨将其称之为“配置树”,一个Configuration对象最终对应着这棵配置树的某个节点,而整棵配置树自然可以由根节点对应的Configuration对象来表示。以键值对体现的“原子配置项”一般对应于配置树中不具有子节点的“叶子节点”。
接下来我们同样以实例的方式来演示如何定义并读取具有层次结构的配置。我们依然沿用上一节的应用场景,不过现在我们不仅仅需要设置日期/时间的格式,还需要设置其他数据类型的格式,比如表示货币的Decimal类型。为此我们定义了如下一个CurrencyDecimalFormatOptions类,它的属性Digits和Symbol分别表示小数位数和货币符号,一个CurrencyDecimalFormatOptions对象依然是利用一个Configuration对象来创建的。
public class CurrencyDecimalFormatOptions { public int Digits { get; set; } public string Symbol { get; set; } public CurrencyDecimalFormatOptions (IConfiguration config) { this.Digits = int.Parse(config["Digits"]); this.Symbol = config["Symbol"]; } }
我们定义了另一个名为FormatOptions的类型来表示针对不同数据类型的格式设置。如下面的代码片段所示,它的两个属性DateTime和CurrencyDecimal分别表示针对日期/时间和货币数字的格式设置。FormatOptions依然具有一个参数类型为IConfiguration接口的构造函数,它的两个属性均在此构造函数中被初始化。值得注意的是初始化这两个属性采用的是当前Configuration的“子配置节”,我们通过指定配置节名称调用GetSection方法获得这两个子配置节。
public class FormatOptions { public DateTimeFormatOptions DateTime { get; set; } public CurrencyDecimalFormatOptions CurrencyDecimal { get; set; } public FormatOptions (IConfiguration config) { this.DateTime = new DateTimeFormatOptions (config.GetSection("DateTime")); this.CurrencyDecimal = new CurrencyDecimalFormatOptions (config.GetSection("CurrencyDecimal")); } }
FormatOptions类型体现的配置具有如下图所示的树形层次化结构。在我们上面演示的实例中,我们通过以一个MemoryConfigurationSource对象来提供原始的配置信息。由于承载原始配置信息的是一个元素类型为KeyValuePair<string, string>的集合,它在物理存储上并不具有树形化的层次结构,那么它如何能够最终提供一个结构化的Configuration对象呢?
解决方案其实很简单,对于一棵完整的配置树,具体的配置信息最终是通过叶子节点来承载的,所以MemoryConfigurationSource只需要在配置字典中保存叶子节点的数据即可。除此之外,为了描述配置树的结构,配置字典需要将对应叶子节点在配置树种的路径作为Key。所以MemoryConfigurationSource可以采用下表所示的配置字典对配置数进行“扁平化”,路径采用冒号(“:”)作为分隔符。如Format:DateTime:LongDatePattern 对应 dddd, MMMM d, yyyy
如下面的代码片段所示,我们按照表1所示的结构创建了一个Dictionary<string, string>对象,并利用它创建出MemoryConfigurationSource对象。在利用ConfigurationBuildr得到表示整个配置的Configuration对象之后,我们调用其GetSection方法得到名称为“Format”的配置节,并利用后者创建一个FormatOptions。
Dictionary<string, string> 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"] = "$", }; IConfiguration configuration = new ConfigurationBuilder() .Add(new MemoryConfigurationSource { InitialData = source }) .Build(); FormatOptions options = new FormatOptions(configuration.GetSection("Format")); DateTimeFormatOptions dateTime = options.DateTime; CurrencyDecimalFormatOptions 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对象之后,为了验证该对象与原始配置数据是否一致,我们依然将它的相关属性打印在控制台上。这个程序之后之后改程序会在控制台上呈现如下所示的输出结果。
DateTime: LongDatePattern : dddd, MMMM d, yyyy LongTimePattern : h:mm:ss tt ShortDatePattern: M/d/yyyy ShortTimePattern: h:mm tt CurrencyDecimal: Digits : 2 Symbol : $
三、将结构化配置直接绑定为对象
在真正的项目开发过程中,我们倾向于像我们演示的两个实例一样通过创建相应的类型(比如演示实例中的DateTimeFormatOptions、CurrencyDecimalOptions和FormatOptions)来定义一组相关的配置选项(Option),我们将定义配置选项(Option)的这些类型称为Option类型。在上面演示的实例中,为了创建这些封装配置的对象,我们都是采用手工读取配置的形式,如果定义的配置项太多的话,逐条读取配置项其实是一项非常繁琐的工作。
对于一个对象来说,如果我们将它的属性视为它的子节点,一个对象同样具有类似于Configuration对象的树形层次结构。如果我们根据某个Option类型的结构来定义配置,或者根据配置的结构来定义这个Option类型,Option类型的属性成员将与某个配置节具有一一对应的关系,那么原则上我们可以自动将配置信息绑定为一个具体的Option对象。
.NET Core的配置系统采用一种叫做“Options Pattern”的编程模式来支持从原始配置到Options对象之间的绑定。这种编程模式涉及的API定义在“Microsoft.Extensions.Options.ConfigurationExtensions”这个NuGet包中,所以我们需要在项目的csproj文件中按照如下的方式添加针对性的依赖。除此之外,“Options Pattern”涉及到对DI的使用,所以我们还需要添加针对NuGet包“Microsoft.Extensions
.DependencyInjection”的依赖。
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
借助于Options Pattern的自动绑定机制,我们无需逐条地读取配置,所以我们可以将这个三个Options类型(DateTimeFormatOptions、CurrencyDecimalOptions和FormatOptions)的构造函数全部删除,只保留其属性成员。在作为程序入口的Main方法中,我们采用如下的方式创建这个表示格式设置的FormatOptions对象。
ConfigureFromConfigurationOptions<FormatOptions> configureFromConfigurationOptions = new ConfigureFromConfigurationOptions<FormatOptions>(config.GetSection("format")); FormatOptions optionss = new ServiceCollection() .ConfigureOptions((object)(configureFromConfigurationOptions)) .BuildServiceProvider() .GetService<IOptions<FormatOptions>>() .Value;
转载:http://www.cnblogs.com/artech/p/new-config-system-01.html


浙公网安备 33010602011771号