物理文件是我們最常用到的原始配置的載體,最佳的配置文件格式主要由三種,它們分別是JSON、XML和INI,對應的配置源類型分別是JsonConfigurationSource、XmlConfigurationSource和IniConfigurationSource。 ...
物理文件是我們最常用到的原始配置的載體,最佳的配置文件格式主要由三種,它們分別是JSON、XML和INI,對應的配置源類型分別是JsonConfigurationSource、XmlConfigurationSource和IniConfigurationSource。 [ 本文已經同步到《ASP.NET Core框架揭秘》之中]
目錄
一、FileConfigurationSource & FileConfigurationProvider
二、JsonConfigurationSource &JsonConfigurationProvider
三、XmlConfiguationSource & XmlConfiguationProvider
四、IniConfigurationSource & IniConfigurationSource
一、FileConfigurationSource & FileConfigurationProvider
上述這三個具體的ConfigurationSource類型具有如下一個相同的基類FileConfigurationSource。
1: public abstract class FileConfigurationSource : IConfigurationSource
2: {
3: public IFileProvider FileProvider { get; set; }
4: public bool Optional { get; set; }
5: public string Path { get; set; }
6: public bool ReloadOnChange { get; set; }
7:
8: public abstract IConfigurationProvider Build(IConfigurationBuilder builder);
9: }
如上面的代碼片段所示,FileConfigurationSource是一個抽象類,它利用一個FileProvider對象來提供原始的配置文件,而Path屬性自然代表配置文件的路徑。Optional屬性表示明當前的FileConfigurationSource是否是可選的配置源,其預設值為False。當某個FileConfigurationSource的Optional屬性為True的時候,如果指定的配置文件路徑不存在,將不會有任何異常被拋出來。由於FileProvider具有監控文件變化的能力,它的ReloadOnChange屬性表示如果被監控的配置文件發生改變後是否需要重新載入配置。
由於FileConfigurationSource總是利用FileProvider來讀取配置文件的內容,所以當我們創建一個具體的FileConfigurationSource對象的時候都需要採用顯式或者隱式的方式指定一個FileProvider對象(關於FileProvider,可以參閱我的“文件系統”博文系列)。我們可以調用擴展方法SetFileProvider將一個預設的FileProvider註冊到ConfigurationBuilder對象上,從相面的代碼片段可以看出註冊的FileProvider被保存到Properties屬性表示的字典對象上,對應的Key為“FileProvider”。
1: public static class FileConfigurationExtensions
2: {
3: private static string FileProviderKey = "FileProvider";
4:
5: public static IFileProvider GetFileProvider(this IConfigurationBuilder builder)
6: {
7: object obj2;
8: if (builder.Properties.TryGetValue(FileProviderKey, out obj2))
9: {
10: return (builder.Properties[FileProviderKey] as IFileProvider);
11: }
12: return new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty);
13: }
14:
15: public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath)
16: {
17: return builder.SetFileProvider(new PhysicalFileProvider(basePath));
18: }
19:
20: public static IConfigurationBuilder SetFileProvider(this IConfigurationBuilder builder, IFileProvider fileProvider)
21: {
22: builder.Properties[FileProviderKey] = fileProvider;
23: return builder;
24: }
25: }
通過隱式方式提供的FileProvider通過調用ConfigurationBuilder的另一個擴展方法GetFileProvider方法獲取。從上面給出的代碼片段我們可以看到,它會優先返回我們註冊的FileProvider。如果這樣的FileProvdier尚未註冊,該方法會返回指向當前應用執行目錄的PhysicalFileProvider對象。除了上述這兩個方法,ConfigurationBuilder還具有另一個名為SetBasePath的方法,該方法採用指定的路徑創建一個PhysicalFileProvider對象並對它進行註冊。
雖然JsonConfigurationSource、XmlConfigurationSource和IniConfigurationSource這些針對通過文件類型的ConfigurationSource會提供不同類型的ConfigurationProvider來讀取對應的配置文件並將讀取的內容轉換成一個配置字典,但是這些ConfigurationProvider都派生與如下一個FileConfigurationProvider類型。FileConfigurationSource和FileConfigurationProvider都定義在“Microsoft.Extensions.Configuration
.FileExtensions”這個NuGet包中。
1: public abstract class FileConfigurationProvider : ConfigurationProvider
2: {
3:
4: public FileConfigurationSource Source { get; private set; }
5:
6: public FileConfigurationProvider(FileConfigurationSource source)
7: {
8: this.Source = source;
9: if (this.Source.ReloadOnChange)
10: {
11: ChangeToken.OnChange(
12: () => this.Source.FileProvider.Watch(this.Source.Path),
13: this.Load);
14: }
15: }
16:
17: public override void Load()
18: {
19: IFileInfo fileInfo = this.Source.FileProvider.GetFileInfo(this.Source.Path);
20:
21: if ((fileInfo == null) || !fileInfo.Exists)
22: {
23: if (!this.Source.Optional)
24: {
25: throw new FileNotFoundException();
26: }
27: base.Data = new Dictionary<string, string>(
28: StringComparer.OrdinalIgnoreCase);
29: }
30: else
31: {
32: using (Stream stream = fileInfo.CreateReadStream())
33: {
34: this.Load(stream);
35: }
36: }
37: base.OnReload();
38: }
39:
40: public abstract void Load(Stream stream);
41: }
我們通過如上所示的代碼片段以簡化的形式模擬了FileConfigurationProvider的實現邏輯。它定義了一個抽象方法Load來完成針對配置文件的讀取和配置字典的生成,該參數代表讀取文件的輸出流。在重寫的Load方法中,它直接利用FileProvider得到描述配置文件的FileInfo對象,並調用此FileInfo對象的CreateReadStream方法得到這個Stream對象。
上面的這個代碼片段還體現了額外一些細節。首先,如果我們將FileConfigurationSource的ReloadOnChange屬性設置為True,意味著我們希望當配置文件發生該表的時候重新載入該文件。FileConfigurationProvider直接利用FileProvider的Watch方法監視配置文件的變換,並將Load方法註冊為回調從而到達配置數據同步的目的。其次,如果指定的配置文件不存在,並且FileConfigurationSource的Optional屬性被設置為True,FileConfigurationProvider是不能拋出FileNotFoundException異常的。
二、JsonConfigurationSource&JsonConfigurationProvider
JsonConfigurationSource代表針對通過JSON文件定義的配置源,該類型定義在NuGet包“Microsoft.Extensions.Configuration.Json”中。如下麵的代碼片段所示,在重寫的Build方法中,如果FileProvider屬性沒有被顯式賦值,它會調用ConfigurationBuilder的擴展方法GetFileProvider得到一個FileProvdier並對該屬性賦值。Build方法最終創建並返回的是一個根據自己創建的JsonConfigurationProvider對象。作為FileConfigurationProvider的繼承者,JsonConfigurationProvider利用重寫的Load方法讀取配置文件的內容並將其轉換成配置字典。
1: public class JsonConfigurationSource : FileConfigurationSource
2: {
3: public override IConfigurationProvider Build(IConfigurationBuilder builder)
4: {
5: base.FileProvider = base.FileProvider ?? builder.GetFileProvider();
6: return new JsonConfigurationProvider(this);
7: }
8: }
9:
10: public class JsonConfigurationProvider : FileConfigurationProvider
11: {
12: public JsonConfigurationProvider(JsonConfigurationSource source);
13: public override void Load(Stream stream);
14: }
“Microsoft.Extensions.Configuration.Json”這個NuGet包為我們定義瞭如下所示的一系列針對IConfigurationBuilder介面的擴展方法AddJsonFile來完成針對JsonConfigurationSource的註冊。如果調用第一個AddJsonFile方法重載,我們可以利用指定的Action<JsonConfigurationSource>對象對創建的JsonConfigurationSource進行初始化。至於後續的AddJsonFile方法重載,實際上就是通過相應的參數初始化JsonConfigurationSource的Path、Optional和ReloadOnChange屬性罷了。
1: public static class JsonConfigurationExtensions
2: {
3: public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource);
4: public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path);
5: public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional);
6: public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional,bool reloadOnChange);
7: }
當使用JSON文件來定義配置的時候,我們會發現不論對於何種數據結構(複雜對象、集合、數組和字典),我們都能通過JSON格式以一種簡單而自然的方式來定義它們。同樣以前面定義的Profile類型為例,我們可以利用如下所示的三個JSON文件分別定義一個完整的Profile對象、一個Profile對象的集合以及一個Key和Value類型分別為字元串和Profile的字典。
Profile類型
1: public class Profile
2: {
3: public Gender Gender { get; set; }
4: public int Age { get; set; }
5: public ContactInfo ContactInfo { get; set; }
6: }
7:
8: public class ContactInfo
9: {