File type configuration basic content

The previous article discussed several core objects of Configuration. This article continues to discuss the related content of File configuration in Configuration. In comparison, file-type configuration is more widely used, and user-defined configuration extensions can also be extended based on file-type configuration. If you need to view the previous article, you can click this

.

We provide three main implementations in the .NET Core file configuration, namely JSON, XML, and INI. Please see the figure below.


DotNET_Core_3.0_source-understanding_Configuration_(2)_0.png

 

As can be seen from the figure, the implementation of these three configurations is the same, of course, other configurations such as command line configuration, environment variable configuration, etc. are similar, understand the implementation of the modified configuration type, and then we will expand based on Consul or ZK. Implementation is very simple.

Abstract extension of file configuration

The abstract extension of the file-type configuration is located in the Microsoft.Extensions.Configuration.FileExtensions component, which is a basic implementation. However, its namespace is Microsoft.Extensions.Configuration, and the Micros oft.Extensions.Configuration extension itself is the underlying implementation of the entire .NET Core Configuration. The File extension is independent of the outside and has experienced the modular design of .NET Core.

FileConfigurationSource

In the Configuration.FileExtensions component, FileConfigurationSource is an abstract class that inherits from IConfigurationSource and contains an abstract method of type IConfigurationProvider, as shown below.

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

The abstract class also includes several important parameters for configuration behavior, file content access, and exception handling.

String Path: the path to the file

Bool Optional: Identifies whether the loaded file is optional.

Bool ReloadOnChange: Whether to reload the configuration source if the file is modified

Int ReloadDelay: load delay, in milliseconds, default is 250 milliseconds

IFileProvider FileProvider: used to get the file content

Action<FileLoadExceptionContext> OnLoadException: file loading exception handling

This class has special handling for FileProvider, that is, if a FileProvider instance is not provided, a physical file provider is created in the nearest existing directory based on the absolute path. The source code is as follows,

   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

This class is an abstract class that inherits from the ConfigurationProvider. It is a base class for loading configuration from the file system. It also inherits IDisposable. Its abstract method is the Load method, which is used to load data from the current Provider in Stream mode.

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

This class also overrides the Load method of the ConfigurationProvider and handles the exceptions in the file loading. The Data property has been mentioned earlier, and no further explanation is given here. The method source code is as follows:

   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?, by comment, we can know that the OnReload() method may change in the new version.
  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:   }

In addition, it has a special method, the constructor of the parameter type is FileConfigurationSource, its main function is to monitor the file, and reload the file and return a value of IDisposable type at the time set by FileConfigurationSource.ReloadDelay, the following is the constructor Source code:

   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

This class is a static class that provides multiple extension methods, mainly based on

  • IConfigurationBuilder
  • IFileProvider
  • Action<FileLoadExceptionContext>

Including the main use of setting or getting the IFileProvider object, as described in the previous article, is stored in the dictionary, it should be noted that if there is no IFileProvider object in the dictionary when Get, it will instantiate a PhysicalFileProvider object. The class is located at 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:   }

Sets the file type provider for the physical file of the specified path. This method is also based on PhysicalFileProvider and returns the IConfigurationBuilder object.

   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:   }

As well as exception handling, you can see that its exception handling will also be stored in the dictionary. If it is not found in the dictionary, it will return null. If this place is used directly, you need to pay attention to the null pointer problem.

   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:   }

This class also has two static private variables, which specify the Key of the file provider and the file loading exception handling key.

   1:  private static string FileProviderKey = "FileProvider";
   2:  private static string FileLoadExceptionHandlerKey = "FileLoadExceptionHandler";

to sum up

File-based configuration also relies on other components of the .NET Core, Microsoft.Extensions.FileProviders and Microsoft.Extensions.Primitives.

The FileProviders component provides a general method for file processing. The Primitives component provides a monitoring mechanism. It also includes two more important structures, StringValues ​​and StringSegment. This article is not discussed for a while. Interested friends can view the component source code by themselves.

Orignal link:https://www.cnblogs.com/edison0621/p/10889325.html