文件型配置基本内容
上一篇文章讨论了Configuration的几个核心对象,本文继续讨论Configuration中关于文件型配置的相关内容。相比较而言,文件型配置的使用场景更加广泛,用户自定义配置扩展也可以基于文件型配置进行扩展。如果需要查看上一篇文章,可以点击
移步 <https://www.cnblogs.com/edison0621/p/10854215.html>。
.NET Core文件型配置中我们提供了三种主要的实现,分别是JSON、XML、INI,请查看下图
 
<https://img2018.cnblogs.com/blog/533598/201905/533598-20190519150805787-510308491.png>
由图可知,这三种配置的实现方式是一样的,当然了其他的配置比如命令行配置、环境变量配置等也是大同小异,理解了改配置类型的实现方式,后面我们再扩展基于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,本文暂时不做讨论,有兴趣的朋友,可以自行查看该组件源码。
热门工具 换一换
