.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源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索