閱讀636 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ASP.NET Core的配置(4):多樣性的配置來源[中篇]

我們在本篇文章中會介紹三種針對物理文件的ConfiguationProvider,它們分別是針對JSON文件的JsonConfiguationProvider,針對XML文件的XmlConfiguationProvider以及針對INI文件的IniConfiguationProvider。對於這三種文件類型(JSON、XML和INI)來說,JSON能夠采用簡單直觀的格式表示具有不同結構的數據,所以它是作為配置最好的選擇。

目錄
MemoryConfigurationProvider
EnvironmentVariablesConfigurationProvider
CommandLineConfigurationProvider
JsonConfigurationProvider
XmlConfiguationProvider
IniConfigurationProvider
自定義ConfigurationProvider

我們可以將配置定義在一個JSON文件中,最終利用JsonConfiguationProvider加載該文件並將以JSON格式表示的配置原始數據轉換成配置字典供配置模型使用。JsonConfiguationProvider定義在“Microsoft.Extensions.Configuration.Json”程序集下,它同樣也是所在NuGet包的名稱。

   1: public class JsonConfigurationProvider : ConfigurationProvider
   2: {
   3:     public JsonConfigurationProvider(string path);
   4:     public JsonConfigurationProvider(string path, bool optional);
   5:  
   6:     public override void Load();    
   7:    
   8:     public string    Path { get; }
   9:     public bool      Optional { get; }
  10: }

如上麵的代碼片斷所示,JsonConfiguationProvider具有兩個隻讀屬性(Path和Optional),前者代表承載原始配置數據的JSON文件所在路徑,後者表明當前的JsonConfiguationProvider是否是可選的ConfigurationProvider,其默認值為False。,在這種情況下它會創建一個空的字典對象作為配置源。

在使用JsonConfiguationProvider的時候,我們可以直接創建這個對象並調用Add方法將其添加到指定的ConfigurationBuilder之中。我們可以直接調用ConfigurationBuilder對象具有如下定義的兩個擴展方法AddJsonFile達到相同的目的。

   1: public static class JsonConfigurationExtensions
   2: {
   3:     public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder configurationBuilder, string path);
   4:     public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder configurationBuilder, string path, bool optional);
   5: }

當使用JSON文件來定義配置的時候,我們會發現不論對於何種數據結構(複雜對象、集合戶者字典),我們都能通過JSON格式以一種簡單而自然的方式來定義它們。同樣以前麵定義的Profile類型為例,我們可以利用如下所示的三個JSON文件分別定義一個完整的Profile對象、一個Profile對象的集合以及一個Key和Value類型分別為字符串和Profile的字典。

Profile.json

   1: {
   2:   "profile": {
   3:     "gender"  : "Male",
   4:     "age"     : "18",
   5:     "contactInfo": {
   6:       "email"        : "foobar@outlook.com",
   7:       "phoneNo"      : "123456789"
   8:     }
   9:   }
  10: }

ProfileCollection.json

   1: {
   2:   "profiles": [
   3:     {
   4:       "gender"       : "Male",
   5:       "age"          : "18",
   6:       "contactInfo": {
   7:         "email"      : "foo@outlook.com",
   8:         "phoneNo"    : "123"
   9:       }
  10:     },
  11:     {
  12:       "gender" : "Male",
  13:       "age"   : "25",
  14:       "contactInfo": {
  15:         "email"      : "bar@outlook.com",
  16:         "phoneNo"    : "456"
  17:       }
  18:     },
  19:     {
  20:       "gender" : "Female",
  21:       "age"   : "40",
  22:       "contactInfo": {
  23:         "email"      : "baz@outlook.com",
  24:         "phoneNo"    : "789"
  25:       }
  26:     }
  27:   ]
  28: }

ProfileDictionary.json

   1: {
   2:   "profiles": {
   3:     "foo": {
   4:       "gender" : "Male",
   5:       "age"   : "18",
   6:       "contactInfo": {
   7:         "email"      : "foo@outlook.com",
   8:         "phoneNo"    : "123"
   9:       }
  10:     },
  11:     "bar": {
  12:       "gender" : "Male",
  13:       "age"   : "25",
  14:       "contactInfo": {
  15:         "email"      : "bar@outlook.com",
  16:         "phoneNo"    : "456"
  17:       }
  18:     },
  19:     "baz": {
  20:       "gender": "Female",
  21:       "age"   : "40",
  22:       "contactInfo": {
  23:         "email"      : "baz@outlook.com",
  24:         "phoneNo"    : "789"
  25:       }
  26:     }
  27:   }
  28: }

對於上麵定義的三個JSON文件,我們可以按照如下的方式創建相應的JsonConfigurationProvider並注冊到ConfigurationBuilder中。我們利用ConfigurationBuilder生成的Configuration對象,並采用配置綁定的方式得到對應的Profile、Profile[]和Dictionary<string,Profile>對象。

   1: Profile profile = new ConfigurationBuilder()
   2:     .AddJsonFile("Profile.json")
   3:     .Build()
   4:     .Get<Profile>("Profile");
   5:  
   6: Profile[] profileArray = new ConfigurationBuilder()
   7:     .AddJsonFile("ProfileCollection.json")
   8:     .Build()
   9:     .Get<Profile[]>("Profiles");
  10:  
  11: Dictionary<string, Profile> profileDictionary = new ConfigurationBuilder()
  12:     .AddJsonFile("ProfileDictionary.json")
  13:     .Build()
  14: .Get<Dictionary<string, Profile>>("Profiles");


二、XmlConfiguationProvider

XML也是一種常用的配置定義形式,它對數據的表達能力甚至強於JSON,基於所有類型的數據結構都可以通過XML表示出來。當我們通過一個XML元素表示一個複雜對象的時候,對象的數據成員定義成當前XML元素的。如果數據成員是一個簡單數據類型,我們還可以選擇將其定義成當前XML元素的(Attribute)。針對一個Profile對象,我們可以采用如下兩種不同的形式來定義。

   1: <Profile>
   2:   <Gender>Male</Gender>
   3:   <Age>18</Age>
   4:   <ContactInfo>
   5:     <Email>foobar@outlook.com</Email>
   6:     <PhoneNo>123456789</PhoneNo>
   7:   </ContactInfo>
   8: </Profile>

或者

   1: <Profile Gender="Male" Age="18">
   2:   <ContactInfo Email="foobar@outlook.com" PhoneNo="123456789"/>
   3: </Profile>

雖然XML對數據結構的表達能力總體要強於JSON,但是對於配置模型的一種數據來源卻有自己的局限性,比如它們對集合的表現形式有點不盡如人意。舉個簡單的例子,對於一個元素類型為Profile的集合,我們可以采用具有如下結構的XML來表現。

   1: <Profiles>
   2:   <Profile Gender="Male" Age="18">
   3:     <ContactInfo Email="foobar@outlook.com" PhoneNo="123"/>
   4:   </Profile>
   5:   <Profile Gender="Male" Age="25">
   6:     <ContactInfo Email="bar@outlook.com" PhoneNo="456"/>
   7:   </Profile>
   8:   <Profile Gender="Male" Age="40">
   9:     <ContactInfo Email="baz@outlook.com" PhoneNo="789"/>
  10:   </Profile>
  11: </Profiles>

但是這段XML卻不能正確地轉換成配置字典,因為表示一個Profile對象的三個XML元素(<Profile>...</Profile>)是“”的,對於由它們表示的三個Profile對象來說,分別表示性別、年齡、電子郵箱地址和電話號碼的。通過前麵的介紹我們知道,如果需要通過配置字典來表示一個Profile對象的集合,我們需要按照如下的方式將集合元素的索引(0、1、2、…)作為路徑的一部分。

   1: 0:Gender
   2: 0:Age
   3: 0:ContactInfo:Email
   4: 0:ContactInfo:PhoneNo
   5:  
   6: 1:Gender
   7: 1:Age
   8: 1:ContactInfo:Email
   9: 1:ContactInfo:PhoneNo
  10:  
  11: 2:Gender
  12: 2:Age
  13: 2:ContactInfo:Email
  14: 2:ContactInfo:PhoneNo

微軟提供了一個不太理想方案來解決這個問題,那就是在表示集合元素的XML元素中添加一個名為(不區分大小寫)的屬性。如果一個XML元素具有一個名為Name的特性,當它被轉換成配置字典的時候,其屬性和子元素對應的路徑會自動將這個屬性值作為前綴。比如我們在<ContactInfo>元素中按照如下的方式添加了一個值為“Foobar”的Name屬性,Email和PhoneNo在配置字典中的Key將具有額外的前綴“Foobar”。

   1: <ContactInfo  Email="foobar@outlook.com" PhoneNo="123"/>
   2:  
   3: Keys:
   4: ContactInfo:Foobar:Email
   5: ContactInfo:Foobar:PhoneNo

為了以XML的方式表示一個Profile集合,我們可以按照如下的方式在<Profile>元素上添加一個Name屬性,並采用元素索引作為該屬性的值。我之所以覺得這是一種不算理想的解決方案,隻要源於兩個因素:

   1: <Profiles>
   2:   <Profile Name="0" Gender="Male" Age="18">
   3:     <ContactInfo Email="foobar@outlook.com" PhoneNo="123"/>
   4:   </Profile>
   5:   <Profile Name="1" Gender="Male" Age="25">
   6:     <ContactInfo Email="bar@outlook.com" PhoneNo="456"/>
   7:   </Profile>
   8:   <Profile Name="2" Gender="Male" Age="40">
   9:     <ContactInfo Email="baz@outlook.com" PhoneNo="789"/>
  10:   </Profile>
  11: </Profiles>

既然這個特性的XML屬性具有自動附加前綴的作用,我們可以利用來表示集合元素的索引,如果需要使用XML來表示一個,我們照樣可以用它來表示字典元素的Key。上麵這段XML同樣可以表示一個Dictionary<string, Profile>(或者Dictionary<int, Profile>)對象,字典元素的Key分別是“0”、“1”和“2”。不過,我們也可以采用如下所示的另一種方式表示一個Dictionary<string, Profile>對象。

   1: <Profiles>
   2:   <Foo Gender="Male" Age="18">
   3:     <ContactInfo Email="foobar@outlook.com" PhoneNo="123"/>
   4:   </Foo>
   5:   <Bar Gender="Male" Age="25">
   6:     <ContactInfo Email="foobar@outlook.com" PhoneNo="123"/>
   7:   </Bar>
   8:   <Baz Gender="Male" Age="18">
   9:     <ContactInfo Email="baz@outlook.com" PhoneNo="789"/>
  10:   </Baz>
  11: </Profiles>

針對XML文件的ConfigurationProvider類型為XmlConfigurationProvider,它定義在程序集“Microsoft.Extensions.Configuration.Xml”,這同樣也是所在NuGet包的名稱。因為同為針對文件的ConfigurationProvider,所以XmlConfigurationProvider具有與JsonConfigurationProvider完全一致的定義。除此之外,我們同樣可以調用相應的擴展方法AddXmlFile根據指定的文件路徑創建出相應的XmlConfigurationProvider對象並將其注冊到指定的ConfigurationBuilder對象上。

   1: public class XmlConfigurationProvider : ConfigurationProvider
   2: {
   3:     public XmlConfigurationProvider (string path);
   4:     public XmlConfigurationProvider (string path, bool optional);
   5:  
   6:     public override void Load();    
   7:    
   8:     public string     Path { get; }
   9:     public bool     Optional { get; }
  10: }
  11:  
  12: public static class XmlConfigurationExtensions
  13: {
  14:    public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder configurationBuilder, string path);
  15:    public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder configurationBuilder, string path, bool optional);
  16: }


三、IniConfigurationProvider

“INI”是“Initialization”的縮寫,INI文件又被稱為初始化文件,它是Windows係統普遍使用的配置文件,同時也被一些Linux和Unix係統所支持。INI文件直接以鍵值對的形式定義配置項,如下所示的代碼片段體現了INI文件的基本格式。總的來說,INI文件以單純的“{Key}={Value}”的形式定義配置項,{Value}可以定義在可選的雙引號中(如果值的前後包括空白字符,必須使用雙引號,否則會被忽略)。

   1: [Section]
   2: key1=value1
   3: key2 = " value2 "
   4: ; comment
   5: # comment
   6: / comment

除了以“的定義的原子配置項外,我們還可以采用“的形式定義配置節對它們進行分組。中括號(“[]”)同時作為下一個的配置節開始的標誌,同時也作為上一個配置結束的標誌,所以采用INI文件定義的配置節並不存在層次化的結構,即沒有“子配置節”的概念。除此之外,我們可以在INI中定義相應的注釋,注釋行前置的字符可以采用。

由於INI文件自身就體現為一個數據字典,所以我們可以采用“路徑化”的Key來定義最終綁定為複雜對象、集合或者字典的配置數據。如果采用INI文件來定義一個Profile對象的基本信息,我們就可以采用如下定義形式。

   1: Gender              = "Male"
   2: Age                 = "18"
   3: ContactInfo:Email   = "foobar@outlook.com"
   4: ContactInfo:PhoneNo = "123456789"

有Profile的配置信息具有兩個層次(Profile>ContactInfo),我們可以按照如下的形式將Emil和PhoneNo定義在配置節“ContactInfo”中,這個INI文件和上麵是完全等效的。

   1: Gender  = "Male"
   2: Age     = "18"
   3:  
   4: [ContactInfo]
   5: Email   = "foobar@outlook.com"
   6: PhoneNo = "123456789"

作為針對INI文件的ConfigurationProvider,IniConfigurationProvider定義在程序集(同時也是NuGet包)“Microsoft.Extensions.Configuration.Ini”中。如下麵的代碼片斷所示,IniConfigurationProvider和前麵兩個同是基於文件的ConfigurationProvider的定義完全一致。ConfigurationBuilder同樣一個用於注冊IniConfigurationProvider的擴展方法AddIniFile。

   1: public class IniConfigurationProvider : ConfigurationProvider
   2: {
   3:     public IniConfigurationProvider (string path);
   4:     public IniConfigurationProvider (string path, bool optional);
   5:  
   6:     public override void Load();    
   7:    
   8:     public string     Path { get; }
   9:     public bool     Optional { get; }
  10: }
  11:  
  12: public static class IniConfigurationExtensions
  13: {
  14:    public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder configurationBuilder, string path);
  15:    public static IConfigurationBuilder AddIniFile (this IConfigurationBuilder configurationBuilder, string path, bool optional);
  16: }

 

ASP.NET Core的配置(1):讀取配置信息
ASP.NET Core的配置(2):配置模型詳解
ASP.NET Core的配置(3): 將配置綁定為對象[上篇]
ASP.NET Core的配置(3): 將配置綁定為對象[下篇]
ASP.NET Core的配置(4):多樣性的配置源[上篇]
ASP.NET Core的配置(4):多樣性的配置源[中篇]
ASP.NET Core的配置(4):多樣性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 14:34:21

  上一篇:go  ASP.NET Core的配置(4):多樣性的配置來源[上篇]
  下一篇:go  ASP.NET Core的配置(4):多樣性的配置來源[下篇]