我們在本篇文章中會介紹三種針對物理文件的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):配置的同步[下篇]
文章列表