前言:
现时咱们对“net core 配置”大体比较注重,姐妹们都需要学习一些“net core 配置”的相关资讯。那么小编在网络上汇集了一些有关“net core 配置””的相关内容,希望同学们能喜欢,姐妹们一起来了解一下吧!今天我们来讲下AspNetCore5中的配置文件读取设计。
首先抛出个问题,如果要我们来设计一个通过的配置文件读取系统,我们应该要怎么设计类呢?传统的思路可以是设计一个文件读取类,包含文件路径,Get,Set和Init方法,用于初始化文件读取并获取值,不同的文件类型设计不同的文件读取类。那么在AspNetCore中是如何设计的呢?
在AspNetCore中是通过Configuration配置模式来实现多种文件类型的配置读取的。大体的思路,与我们上面提到的差不多。
首先我们来看下Configuration配置模式概览图:
以下是几个重要的类:
ConfigurationBuilder:ConfigurationBuilder类用于在应用启动时添加相应的ConfigurationSource,例如在应用启动时通过的扩展方法添加:AddJson(“appsettings.json”)或者AddCommandLine(args).ConfigurationSource: ConfigurationSource是配置数据源,对于每种不同类型的配置文件提供了不同的配置提供者。ConfigurationProvider: 配置提供者。用于根据配置文件初始化配置值,并将配置键值对形成Key:Value的形式。ConfigurationRoot: 配置根。由于应用可以添加多种程序源,所有持有了这些数据源。并提供了获取和设置键值的方法。获取值,倒序遍历所有的ConfigurationProvider来获取相应Key表示的值。一旦某一个配置提供者有值,就返回该值。注意,后加入的ConfigurationProvider将覆盖前面的配置值。设置值,遍历所有的ConfigurationProvider,并将key所代表的value设置成这个值。ConfigurationSection:配置节点类。用于获取相应key节点下的所有子节点配置项。为ConfigurationRoot提供获取子配置节点的功能。
Configuration模式UML图:
源码解析:
首先来看下源码流程概览:
AspNetCore中,配置模式是在Program.cs类中的Host.CreateDefaultBuilder方法中调用扩展方法来注入ConfigurationSource和并在Build的时候将配置源注入到容器的。
接下来以添加环境变量参数为例。
让我们以Program.cs开始:
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方法中:
//Microsoft.Extensions.Hosting.cspublic static IHostBuilder CreateDefaultBuilder(string[] args){ HostBuilder hostBuilder = new HostBuilder(); ...... hostBuilder.ConfigureHostConfiguration((Action<IConfigurationBuilder>) (config => { config.AddEnvironmentVariables("DOTNET_"); ...... })); ...... return (IHostBuilder) hostBuilder;}
IConfigurationBuilder的AddEnvironmentVariables扩展方法为ConfigurationBuilder添加了环境变量的支持。
//Microsoft.Extensions.Configuration.EnvironmentVariablesExtensions.cspublic static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder,string prefix){ configurationBuilder.Add(new EnvironmentVariablesConfigurationSource() { Prefix = prefix }); return configurationBuilder;}
AddEnvironmentVariables方法添加了EnvironmentVariablesConfigurationSource环境变量数据源,并通过构造传参来确定所要获取的环境变量的前缀。
本例中,构造函数传递的是”DOTNET_”字符串,表示获取环境变量中以”DOTNET_”开头的环境变量。
Ok,现在框架已经把EnvironmentVariablesConfigurationSource数据源加入到了ConfigurationBuilder中,前面的概览图我们知道框架在加入完后会调用ConfigurationBuilder的Builder方法来初始化数据源。
//Microsoft.Extensions.Hosting.HostBuilder.csprivate void BuildAppConfiguration(){ IConfigurationBuilder configBuilder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true); foreach (var buildAction in _configureAppConfigActions) { buildAction(_hostBuilderContext, configBuilder); } //构建数据源配置类 _appConfiguration = configBuilder.Build(); _hostBuilderContext.Configuration = _appConfiguration;}
在ConfigurationBuilder的Builder方法中初始化数据源有以下几个步骤:
1:遍历应用程序中添加的所有数据源,执行其HostBuilder的Build方法创建相对应的ConfigurationProvider
2:创建ConfigurationRoot对象,将第1步创建的ConfigurationProvider集合通过构造传参的方式传入ConfigurationRoot对象中,在ConfigurationRoot的构造函数中对遍历该集合,并依次对ConfigurationProvider执行Load()方法进行初始化数据源操作。
ConfigurationBuilder的Builder方法:
//Microsoft.Extensions.Configuration.ConfigurationBuilder.cspublic IConfigurationRoot Build(){ var providers = new List<IConfigurationProvider>(); //遍历所有的数据源 foreach (IConfigurationSource source in Sources) { //创建IConfigurationProvider实例 IConfigurationProvider provider = source.Build(this); providers.Add(provider); } //创建ConfigurationRoot对象 return new ConfigurationRoot(providers);}
ConfigurationRoot对象同时包含了根据key获取和设置值的方法,该类为最终注入到依赖注入容器的类,并通过Get方法获取初始化后的值 。
//Microsoft.Extensions.Configuration.Configuration.cspublic class ConfigurationRoot : IConfigurationRoot, IDisposable{ private readonly IList<IConfigurationProvider> _providers; private readonly IList<IDisposable> _changeTokenRegistrations; private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken(); public ConfigurationRoot(IList<IConfigurationProvider> providers) { if (providers == null) { throw new ArgumentNullException(nameof(providers)); } _providers = providers; _changeTokenRegistrations = new List<IDisposable>(providers.Count); //遍历所有的IConfigurationProvider实例 foreach (IConfigurationProvider p in providers) { //重点!执行初始化方法进行配置参数的初始化 p.Load(); _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged())); } } public IEnumerable<IConfigurationProvider> Providers => _providers; //索引器模式获取值 public string this[string key] { get { //根据key获取值时,倒序遍历所有的ConfigurationProvider,取到第一个值就返回 //注意:由于添加数据源顺序的不同,有可能会造成变量被覆盖的情况。 for (int i = _providers.Count - 1; i >= 0; i--) { IConfigurationProvider provider = _providers[i]; if (provider.TryGet(key, out string value)) { return value; } } return null; } set { if (!_providers.Any()) { throw new InvalidOperationException(SR.Error_NoSources); } //对key所代表的value设置值。 //遍历每一个IConfigurationprovider实例,并赋值。 foreach (IConfigurationProvider provider in _providers) { provider.Set(key, value); } } } public IEnumerable<IConfigurationSection> GetChildren() => this.GetChildrenImplementation(null); public IChangeToken GetReloadToken() => _changeToken; public IConfigurationSection GetSection(string key)=> new ConfigurationSection(this, key); .....}
我们来看下EnvironmentVariablesProvider类:
//Microsoft.Extensions.Configuration.EnvironmentVariables.EnvironmentVariablesConfigurationProvider.cspublic class EnvironmentVariablesConfigurationProvider : ConfigurationProvider{ ...... private readonly string _prefix; public EnvironmentVariablesConfigurationProvider() : this(string.Empty) { } public EnvironmentVariablesConfigurationProvider(string prefix) { _prefix = prefix ?? string.Empty; } public override void Load() { Load(Environment.GetEnvironmentVariables()); } internal void Load(IDictionary envVariables) { var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); IEnumerable<DictionaryEntry> filteredEnvVariables = envVariables .Cast<DictionaryEntry>() .SelectMany(AzureEnvToAppEnv) .Where(entry => ((string)entry.Key).StartsWith(_prefix, StringComparison.OrdinalIgnoreCase)); foreach (DictionaryEntry envVariable in filteredEnvVariables) { string key = ((string)envVariable.Key).Substring(_prefix.Length); data[key] = (string)envVariable.Value; } Data = data; } ......}
EnvironmentConfigurationProvider类在Load方法的作用:
对当前机器的所有环境变量进行遍历,并根据传入的”DOTNET_”前缀来筛选出符合条件的环境变量。将符合条件的环境变量加入父类的Dictionary<string,string>字典中保存起来。
至此,环境变量的初始化工作完成。
初始化完成后,HostBuilder的Build方法在ConfigurationBuilder执行完后Build方法后,得到的IConfiguration类的实例,并在创建依赖注入容器的时候,将该ConfigurationRoot类以单例的形式注入到了依赖注入容器中,后续便可以通过依赖注入的形式来访问配置的变量了。
//Microsoft.Extensions.Hosting.HostBuilder.csprivate void BuildAppConfiguration(){ IConfigurationBuilder configBuilder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true); ...... _appConfiguration = configBuilder.Build();//构造IConfiguration配置实例 ......} private void CreateServiceProvider() { ...... services.AddSingleton(_ => _appConfiguration);//将IConfiguration实例注入至依赖注入容器中 ...... }
至此,ConfigurationProvider模式介绍完成。
使用方式:在相关类上通过构造传入IConfiguration接口,依赖注入容器会解析出(IConfiguration)ConfigurationRoot对象,通过ConfigurationRoot的索引器方法即可查询到相关的值。
标签: #net core 配置