真实项目中涉及的配置大都具有一个结构化的层次结构,所以在配置模型中的Configuration对象同样具有这样的结构。结构化的配置具有一个树形层次结构,而一个Configuration对象表示的是组成这棵配置树的某个节点,这棵配置树则可以通过作为根节点的Configuration对象来体现。体现为键值对的原子配置项一般至存在于作为叶子节点的Configuration对象中,非叶子节点的Configuration包含一组子节点,而每个子节点同样是一个Configuration对象。
接下来我们同样以实例的方式来演示如何定义并读取具有层次化结构的配置。我们依然沿用上一节的应用场景,现在我们不仅仅需要设置日期/时间的格式,还需要设置其他数据类型的格式,比如表示货币的Decimal类型。为此我们定义了如下一个CurrencyDecimalFormatSettings类,它的属性Digits和Symbol分别表示小数位数和货币符号,一个CurrencyDecimalFormatSettings对象依然是利用一个表示配置的Configuration对象来创建的。
Digits { get; set; }Symbol { get; set; }.Digits = int.Parse(configuration["Digits"]); 8: this.Symbol = configuration["Symbol"]; 9: } 10: }
我们定义了另一个名为FormatSettings的类型来表示针对不同数据类型的格式设置。如下面的代码片段所示,它的两个属性DateTime和CurrencyDecimal分别表示针对日期/时间和货币数字的格式设置。FormatSettings依然具有一个参数类型为IConfiguration接口的构造函数,它的两个属性均在此构造函数中被初始化。值得注意的是初始化这两个属性采用的是当前Configuration的“子配置节”,通过指定配置节名称调用GetSection方法获得。
FormatSettingsCurrencyDecimalFormatSettings CurrencyDecimal { get; set; }.DateTime = new DateTimeFormatSettings(configuration.GetSection("DateTime")); 9: this.CurrencyDecimal = new CurrencyDecimalFormatSettings(configuration.GetSection("CurrencyDecimal")); 10: } 11: }
在我们上面演示的实例中,我们通过以一个MemoryConfigurationProvider对象来提供原始的配置信息。由于承载原始配置信息的是一个元素类型为KeyValuePair<string, string>的集合,所以原始配置在物理存储上并不具有树形化的层次结构,那么它如何能够最终提供一个结构化的Configuration对象呢?其实很简单,虽然MemoryConfigurationProvider对象只能将配置信息存储为简单的“数据字典”,但是如果将Configuration对象在配置树中体现的路径作为Key,这个数据字典在逻辑上实际上就具有了一棵树的结构。实际上MemoryConfigurationProvider就是这么做的,这体现在我们如下所示的程序之中。
Main(string[] args) 4: { 5: Dictionary<string, string> source = new Dictionary<string, string> 6: { 7: ["Format:DateTime:LongDatePattern"] = "dddd, MMMM d, yyyy", 8: ["Format:DateTime:LongTimePattern"] = "h:mm:ss tt", 9: ["Format:DateTime:ShortDatePattern"] = "M/d/yyyy", 10: ["Format:DateTime:ShortTimePattern"] = "h:mm tt", 11: 12: ["Format:CurrencyDecimal:Digits"] = "2", 13: ["Format:CurrencyDecimal:Symbol"] = "$", 14: }; 15: IConfiguration configuration = new ConfigurationBuilder() 16: .Add(new MemoryConfigurationProvider(source)) 17: .Build(); 18: 19: FormatSettings settings = new FormatSettings(configuration.GetSection("Format")); 20: Console.WriteLine("DateTime:"); 21: Console.WriteLine("\t{0,-16}: {1}", "LongDatePattern", settings.DateTime.LongDatePattern); 22: Console.WriteLine("\t{0,-16}: {1}", "LongTimePattern", settings.DateTime.LongTimePattern); 23: Console.WriteLine("\t{0,-16}: {1}", "ShortDatePattern", settings.DateTime.ShortDatePattern); 24: Console.WriteLine("\t{0,-16}: {1}\n", "ShortTimePattern", settings.DateTime.ShortTimePattern); 25: 26: Console.WriteLine("CurrencyDecimal:"); 27: Console.WriteLine("\t{0,-16}: {1}", "Digits", settings.CurrencyDecimal.Digits); 28: Console.WriteLine("\t{0,-16}: {1}", "Symbol", settings.CurrencyDecimal.Symbol); 29: } 30: }
如上面的代码片段所示,创建MemoryConfigurationProvider对象采用的字典对象包含6个基本的配置项,为了让它们在逻辑上具有一个树形化层次结构,所以的Key实际上体现了每个配置项所在配置节在配置树中的路径,路径采用冒号(“:”)进行分割。改程序执行之后会在控制台上呈现如下所示的输出结果。
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 : $
三、将结构化配置直接绑定为对象