说到配置,绝大部分系统都会有配置,不需要配置的系统是非常少的,想想以前做.net 开发时,我们常常将配置放到web.config中,然后使用ConfigurationManager去读取。

  初次接触到.net core 的同学,在项目中看到有一个appsettings.json文件,确实这个appsettings.json文件是做配置用的,所以想当然的把它看做.net 开发中的web.config一样,但是我们要清除,.net core并不依赖appsettings.json文件中的配置。

  .net core 提供了一种非常灵活的配置方式,大部分时候,我们只需要关注DI容器中的IConfiguration接口实例对象就可以了,下面具体介绍。

  这里介绍的.net core版本是3.1,源码地址:https://github.com/dotnet/extensions/tree/v3.1.12/src/Configuration

 

  一、原理

  要介绍原理,先看与配置相关的几个接口及它们的实现类:

  IConfigurationBuilder

  配置建造者接口,我们使用它去创建配置对象,有一个实现类:ConfigurationBuilder

  IConfiguration

  表示配置集合的接口,一般的,程序通过从DI获取IConfiguration接口的实例来获取配置

  IConfigurationRoot

  IConfiguration的子接口,表示配置的根节点,换句话说,IConfigurationBuilder创建的第一个配置对象就是IConfigurationRoot接口对象,它的实现类是:ConfigurationRoot

  IConfigurationSection

  IConfiguration的子接口,表示配置的一个节点,包含节点名、节点路径、值等等,配置节点分隔默认是冒号(:),它的实现类是:ConfigurationSection

  IConfigurationSource

  配置来源接口,IConfigurationSource接口的实现类都很简单,主要用于结合Options创建配置提供者IConfigurationProvider,一般的,它的作用可以认为就是接收参数,然后在创建IConfigurationProvider时将参数传进去。

  但是在读取来自文件的配置时,推荐继承抽象类:FileConfigurationSource ,其它的就直接实现 IConfigurationSource 就可以了,然后添加到 IConfigurationBuilder 的配置源中去。

  IConfigurationProvider

  配置信息的具体提供者,这个就是提供配置的获取、更新等等操作的接口,有两个重要的抽象类:ConfigurationProviderFileConfigurationProvider 

  一般的,如果我们需要集成自己的配置,需要实现这个 IConfigurationSource 接口和 IConfigurationProvider 接口,如果我们的配置和文件有关,建议通过继承 FileConfigurationSource 两个 FileConfigurationProvider 两个抽象类来实现 IConfigurationSource 和 IConfigurationProvider接口,因为这两个抽象类已经提供了一些我们可能需要的功能,比如,它们可以监听文件状态,如果文件内容被修改,则可以重新加载配置。如果配置不来自文件,配置来源可以直接实现 IConfigurationSource 接口,而通过继承 ConfigurationProvider 来实现 IConfigurationProvider 接口。

   于是乎,将它们串接起来,流程就是这样的:

  1、提供一个实现了 IConfigurationProvider 接口的配置提供类,它需要提供配置的读取以及更新等操作

  2、提供一个 IConfigurationSource 接口实现类,它负责创建 IConfigurationProvider 。

  3、创建一个 IConfigurationBuilder 配置建造者对象,然后将 IConfigurationSource 添加进配置构造者中,这里我们一般都采用 IConfigurationBuilder 的拓展方法来实现。

  4、使用 IConfigurationBuilder 构造一个 IConfigurationRoot ,然后使用这个 IConfigurationRoot 去操作配置。

  这是一般流程,而.net core的配置是一个拓展模块,也就是说我们可以在控制台等其他项目中引用,只需要安装包:Microsoft.Extensions.Configuration

  为了更好的说明,我们可以先看IConfiguration在WebHost中是怎么集成的,已.net core 3.1为例,它的Program是这样的:  

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>(); 
                });
    }

  看看Host.CreateDefaultBuilder()方法(源码),源码是这样的:

    public static IHostBuilder CreateDefaultBuilder(string[] args)
    {
        ...

        builder.ConfigureAppConfiguration((hostingContext, config) =>
        {
            var env = hostingContext.HostingEnvironment;

            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

            if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
            {
                var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                if (appAssembly != null)
                {
                    config.AddUserSecrets(appAssembly, optional: true);
                }
            }

            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })

        ...
    }

  现在可以看出为什么appsettings.json是默认的配置文件了,ConfigureAppConfiguration方法就是对配置的构造过程,这里默认最多会加载5个配置源(也就是上面config.AddXXXXX()部分,后面具体介绍)。

  而ConfigureAppConfiguration的实现就是将传进去的委托保存(源码):  

    public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
        return this;
    }

  这里保存是将委托放到一个List中,也就是说ConfigureAppConfiguration方法可以多次调用,我们可以添加我们自己的配置了,在Build时就会按顺序来调用(源码):  

    public IHost Build()
    {
        ...
        BuildAppConfiguration();
        ...
    }

  而BuildAppConfiguration方法则是最终构造配置的过程(源码):  

    private void BuildAppConfiguration()
    {
        var configBuilder = new ConfigurationBuilder()
            .SetBasePath(_hostingEnvironment.ContentRootPath)
            .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);

        foreach (var buildAction in _configureAppConfigActions)
        {
            buildAction(_hostBuilderContext, configBuilder);
        }
        _appConfiguration = configBuilder.Build();
        _hostBuilderContext.Configuration = _appConfiguration;
    }

  可以看到.net core内部也是直接实例化一个ConfigurationBuilder来构造配置的,而它的Build方法则返回的是一个 IConfigurationRoot 接口对象(源码),剩下的就是使用 IConfigurationRoot 接口对象来读取更新配置了。  

    public IConfigurationRoot Build()
    {
        var providers = new List<IConfigurationProvider>();
        foreach (var source in Sources)
        {
            var provider = source.Build(this);
            providers.Add(provider);
        }
        return new ConfigurationRoot(providers);
    }

 

  二、内置的配置方式

  官方在配置方法,提供了一些默认的配置源,它们都是通过IConfigurationBuilder的拓展方法来集成配置源,这也很好的给我们展示了如何添加自己的配置源。

  官方默认提供的配置源有:

  Json文件

  NuGet安装包:Microsoft.Extensions.Configuration.Json

  通过 IConfigurationBuilder的AddJsonFile和AddJsonStream两个拓展方法来集成(源码),有多个重载,各参数的含义如下:  

    provider:提供json文件的一些信息及功能操作,比如所在结构目录,监听文件状态等等,默认默认值:new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty)
    path:json文件路径
    optional:表示json文件是否是可选的,如果未false,那么当json文件不存在时则会抛出异常
    reloadOnChange:表示是否在文件内容修改后重新加载配置,如果未false,表示不重新加载
    stream:json文件流

  比如有一个json文件(注意文件位置):  

  
{
  "Hello": {
    "Microsoft": {
      "Extensions": "Configuration"
    }
  }
}
configuration.json

相关文章: