.NET Core 3.0之深刻源碼理解Configuration(二)

文件型配置基本內容

上一篇文章討論了Configuration的幾個核心對象,本文繼續討論Configuration中關於文件型配置的相關內容。相比較而言,文件型配置的使用場景更加普遍,用戶自定義配置擴展也能夠基於文件型配置進行擴展。若是須要查看上一篇文章,能夠點擊移步微信

.NET Core文件型配置中咱們提供了三種主要的實現,分別是JSON、XML、INI,請查看下圖ide

由圖可知,這三種配置的實現方式是同樣的,固然了其餘的配置好比命令行配置、環境變量配置等也是大同小異,理解了改配置類型的實現方式,後面咱們再擴展基於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類型的抽象方法,以下所示ui

 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);

該抽象類中還包括了幾個比較重要的參數,分別用於配置性行爲、文件內容訪問以及異常處理。this

string Path:文件的路徑spa

bool Optional:標識加載的文件是不是可選的.net

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,本文暫時不作討論,有興趣的朋友,能夠自行查看該組件源碼。


本文分享自微信公衆號 - DotNet技術平臺(DotNetCore_Moments)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索