文章出處

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

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

一、JsonConfiguationProvider

我們可以將配置定義在一個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。如果Optional為True,在創建JsonConfiguationProvider是指定一個不存在的文件將不會拋出異常,在這種情況下它會創建一個空的字典對象作為配置源。

在使用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對象來說,分別表示性別、年齡、電子郵箱地址和電話號碼的四個葉子節點的路徑是完全一樣的,所以根據無法作為配置字典的Key。通過前面的介紹我們知道,如果需要通過配置字典來表示一個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元素中添加一個名為Name(不區分大小寫)的屬性。如果一個XML元素具有一個名為Name的特性,當它被轉換成配置字典的時候,其屬性和子元素對應的路徑會自動將這個屬性值作為前綴。比如我們在<ContactInfo>元素中按照如下的方式添加了一個值為“Foobar”的Name屬性,Email和PhoneNo在配置字典中的Key將具有額外的前綴“Foobar”。

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

為了以XML的方式表示一個Profile集合,我們可以按照如下的方式在<Profile>元素上添加一個Name屬性,并采用元素索引作為該屬性的值。我之所以覺得這是一種不算理想的解決方案,只要源于兩個因素:其一,直接賦予名為“Name”(不包含命名空間)的XML屬性特殊的語義和行為是不合理的,假如Profile同樣具有一個Name屬性(這個屬性實在太常見了),我們將不能用XML屬性來表示它;其二,對于由這段XML生成的Configuration,表示Profile集合的路徑是“Profiles:Profile”而不是“Profiles”,是否覺得很奇怪。

   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

除了以“{Key}={Value}”的定義的原子配置項外,我們還可以采用“[{SectionName}]”的形式定義配置節對它們進行分組。中括號(“[]”)同時作為下一個的配置節開始的標志,同時也作為上一個配置結束的標志,所以采用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):配置的同步[下篇]


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()