文件型配置基本內容 上一篇文章討論了Configuration的幾個核心對象,本文繼續討論Configuration中關於文件型配置的相關內容。相比較而言,文件型配置的使用場景更加廣泛,用戶自定義配置擴展也可以基於文件型配置進行擴展。如果需要查看上一篇文章,可以點擊移步。 .NET Core文件型配 ...
文件型配置基本內容
上一篇文章討論了Configuration的幾個核心對象,本文繼續討論Configuration中關於文件型配置的相關內容。相比較而言,文件型配置的使用場景更加廣泛,用戶自定義配置擴展也可以基於文件型配置進行擴展。如果需要查看上一篇文章,可以點擊移步。
.NET Core文件型配置中我們提供了三種主要的實現,分別是JSON、XML、INI,請查看下圖
由圖可知,這三種配置的實現方式是一樣的,當然了其他的配置比如命令行配置、環境變數配置等也是大同小異,理解了改配置類型的實現方式,後面我們再擴展基於Consul或者ZK的實現,就非常簡單了。
文件型配置的抽象擴展
文件型配置的抽象擴展位於Microsoft.Extensions.Configuration.FileExtensions組件中,該擴展是一個基礎實現。不過其命名空間是Microsoft.Extensions.Configuration,而Micros oft.Extensions.Configuration擴建本身又是整個.NET Core Configuration的基礎實現。將File擴展獨立於外部,體驗了.NET Core的模塊化設計。
FileConfigurationSource
Configuration.FileExtensions組件中,FileConfigurationSource是繼承於IConfigurationSource的一個抽象類,包含了一個IConfigurationProvider類型的抽象方法,如下所示
1: /// <summary>
2: /// Builds the <see cref="IConfigurationProvider"/> for this source.
3: /// </summary>
4: /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
5: /// <returns>A <see cref="IConfigurationProvider"/></returns>
6: public abstract IConfigurationProvider Build(IConfigurationBuilder builder);
該抽象類中還包括了幾個比較重要的參數,分別用於配置性行為、文件內容訪問以及異常處理。
string Path:文件的路徑
bool Optional:標識載入的文件是否是可選的
bool ReloadOnChange:如果文件發生修改,是否重新載入配置源
int ReloadDelay:載入延遲,單位是毫秒,預設是250毫秒
IFileProvider FileProvider:用於獲取文件內容
Action<FileLoadExceptionContext> OnLoadException:文件載入異常處理
該類對FileProvider有特殊處理,就是如果沒有提供FileProvider實例,則會基於絕對路徑,在最近的現有目錄中創建物理文件提供程式。源碼如下,
1: /// <summary>
2: /// If no file provider has been set, for absolute Path, this will creates a physical file provider
3: /// for the nearest existing directory.
4: /// </summary>
5: public void ResolveFileProvider()
6: {
7: if (FileProvider == null &&
8: !string.IsNullOrEmpty(Path) &&
9: System.IO.Path.IsPathRooted(Path))
10: {
11:
var directory = System.IO.Path.GetDirectoryName(Path);
12: var pathToFile = System.IO.Path.GetFileName(Path);
13: while (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
14: {
15: pathToFile = System.IO.Path.Combine(System.IO.Path.GetFileName(directory), pathToFile);
16: directory = System.IO.Path.GetDirectoryName(directory);
17: }
18: if (Directory.Exists(directory))
19: {
20: FileProvider = new PhysicalFileProvider(directory);
21: Path = pathToFile;
22: }
23: }
24: }
FileConfigurationProvider
該類是繼承於ConfigurationProvider的抽象類,是從文件系統載入配置的基類,同時還繼承了IDisposable,其抽象方法是Load方法,用於從當前的Provider中以Stream方式載入數據
1: /// <summary>
2: /// Loads this provider's data from a stream.
3: /// </summary>
4: /// <param name="stream">The stream to read.</param>
5: public abstract void Load(Stream stream);
該類還重寫了ConfigurationProvider的Load方法,並對文件載入中的異常做了處理,Data屬性在前文有提到過,此處不再做其他說明。方法源碼如下所示:
1: private void Load(bool reload)
2: {
3: var file = Source.FileProvider?.GetFileInfo(Source.Path);
4: if (file == null || !file.Exists)
5: {
6: if (Source.Optional || reload) // Always optional on reload
7: {
8: Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
9: }
10: else
11: {
12: var error = new StringBuilder($"The configuration file '{Source.Path}' was not found and is not optional.");
13: if (!string.IsNullOrEmpty(file?.PhysicalPath))
14: {
15: error.Append($" The physical path is '{file.PhysicalPath}'.");
16: }
17: HandleException(new FileNotFoundException(error.ToString()));
18: }
19: }
20: else
21: {
22: // Always create new Data on reload to drop old keys
23: if (reload)
24: {
25: Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
26: }
27: using (var stream = file.CreateReadStream())
28: {
29: try
30: {
31: Load(stream);
32: }
33: catch (Exception e)
34: {
35: HandleException(e);
36: }
37: }
38: }
39: // REVIEW: Should we raise this in the base as well / instead?,通過註釋,我們可以知道OnReload()方法可能會在新版中發生變化
40: OnReload();
41: }
42:
43: /// <summary>
44: /// Loads the contents of the file at <see cref="Path"/>.
45: /// </summary>
46: /// <exception cref="FileNotFoundException">If Optional is <c>false</c> on the source and a
47: /// file does not exist at specified Path.</exception>
48: public override void Load()
49: {
50: Load(reload: false);
51: }
另外它還有一個特殊方法,就是參數類型為FileConfigurationSource的構造函數,其主要功能是監控文件,併在FileConfigurationSource.ReloadDelay設置的時間里重新載入文件並返回一個IDisposable類型的值,以下是該構造函數的源碼:
1: /// <summary>
2: /// Initializes a new instance with the specified source.
3: /// </summary>
4: /// <param name="source">The source settings.</param>
5: public FileConfigurationProvider(FileConfigurationSource source)
6: {
7: if (source == null)
8: {
9: throw new ArgumentNullException(nameof(source));
10: }
11: Source = source;
12:
13: if (Source.ReloadOnChange && Source.FileProvider != null)
14: {
15: _changeTokenRegistration = ChangeToken.OnChange(
16: () => Source.FileProvider.Watch(Source.Path),
17: () => {
18: Thread.Sleep(Source.ReloadDelay);
19: Load(reload: true);
20: });
21: }
22: }
FileConfigurationExtensions
該類是一個靜態類,其提供了的多個擴展方法,主要基於
- IConfigurationBuilder
- IFileProvider
- Action<FileLoadExceptionContext>
包括主要用於設置或獲取IFileProvider對象,前文有介紹過,是存儲於字典之中,需要註意的是,在Get的時候如果字典中並不存在IFileProvider對象,則會實例化一個PhysicalFileProvider對象出來,該類位於Microsoft.Extensions.FileProviders.PhysicalFileProvider
1: /// <summary>
2: /// Sets the default <see cref="IFileProvider"/> to be used for file-based providers.
3: /// </summary>
4: /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
5: /// <param name="fileProvider">The default file provider instance.</param>
6: /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
7: public static IConfigurationBuilder SetFileProvider(this IConfigurationBuilder builder, IFileProvider fileProvider)
8: {
9: if (builder == null)
10: {
11: throw new ArgumentNullException(nameof(builder));
12: }
13:
14: builder.Properties[FileProviderKey] = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
15: return builder;
16: }
17:
18: /// <summary>
19: /// Gets the default <see cref="IFileProvider"/> to be used for file-based providers.
20: /// </summary>
21: /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
22: /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
23: public static IFileProvider GetFileProvider(this IConfigurationBuilder builder)
24: {
25: if (builder == null)
26: {
27: throw new ArgumentNullException(nameof(builder));
28: }
29:
30: if (builder.Properties.TryGetValue(FileProviderKey, out object provider))
31: {
32: return provider as IFileProvider;
33: }
34:
35: return new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty);
36: }
為指定路徑的物理文件設置文件型Provider,該方法同樣基於PhysicalFileProvider,並返回IConfigurationBuilder對象
1: /// <summary>
2: /// Sets the FileProvider for file-based providers to a PhysicalFileProvider with the base path.
3: /// </summary>
4: /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
5: /// <param name="basePath">The absolute path of file-based providers.</param>
6: /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
7: public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath)
8: {
9: if (builder == null)
10: {
11: throw new ArgumentNullException(nameof(builder));
12: }
13:
14: if (basePath == null)
15: {
16: throw new ArgumentNullException(nameof(basePath));
17: }
18:
19: return builder.SetFileProvider(new PhysicalFileProvider(basePath));
20: }
以及異常處理,可以看到其異常處理也會存放於字典中,如果字典中找不到,就會返回空,這個地方如果直接使用,需要註意空指針問題。
1: /// <summary>
2: /// Sets a default action to be invoked for file-based providers when an error occurs.
3: /// </summary>
4: /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
5: /// <param name="handler">The Action to be invoked on a file load exception.</param>
6: /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
7: public static IConfigurationBuilder SetFileLoadExceptionHandler(this IConfigurationBuilder builder, Action<FileLoadExceptionContext> handler)
8: {
9: if (builder == null)
10: {
11: throw new ArgumentNullException(nameof(builder));
12: }
13:
14: builder.Properties[FileLoadExceptionHandlerKey] = handler;
15: return builder;
16: }
17:
18: /// <summary>
19: /// Gets the default <see cref="IFileProvider"/> to be used for file-based providers.
20: /// </summary>
21: /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
22: /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
23: public static Action<FileLoadExceptionContext> GetFileLoadExceptionHandler(this IConfigurationBuilder builder)
24: {
25: if (builder == null)
26: {
27: throw new ArgumentNullException(nameof(builder));
28: }
29:
30: if (builder.Properties.TryGetValue(FileLoadExceptionHandlerKey, out object handler))
31: {
32: return handler as Action<FileLoadExceptionContext>;
33: }
34:
return null;
35: }
該類還有兩個靜態私有變數,指定了文件Provider的Key以及文件載入異常處理Key。
1: private static string FileProviderKey = "FileProvider";
2: private static string FileLoadExceptionHandlerKey = "FileLoadExceptionHandler";
總結
文件型配置還依賴於.NET Core的其他組件Microsoft.Extensions.FileProviders和Microsoft.Extensions.Primitives。
FileProviders組件提供了文件處理的一般方法,Primitives組件提供了監控機制,同時還包括兩個比較重要的結構體StringValues和StringSegment,本文暫時不做討論,有興趣的朋友,可以自行查看該組件源碼。