说到配置,绝大部分系统都会有配置,不需要配置的系统是非常少的,想想以前做.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
配置信息的具体提供者,这个就是提供配置的获取、更新等等操作的接口,有两个重要的抽象类:ConfigurationProvider 和 FileConfigurationProvider
一般的,如果我们需要集成自己的配置,需要实现这个 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" } } }