前言

在之前的文章.Net Core Configuration源码探究一文中我们曾解读过Configuration的工作原理,也.Net Core Configuration Etcd数据源一文中探讨过为Configuration自定义数据源需要哪些操作。由于Configuration配置系统也是.Net Core的核心,其中也包含了许多细节,其中通过启动命令行CommandLine、环境变量、配置文件或定义其他数据源的形式,其实都是适配到配置系统中,我们都可以通过Configuration去读取它们的数据,但是在程序默认的情况下他们读取的优先级到底是怎么样的呢?接下来我们就一起来研究一下。

代码演示

由于Configuration数据操作是我们实操代码过程中不可或缺的环节,所以我们先通过代码的形式来看一下,它的读取顺序到底是什么样子的,首先我们建立一个示例,在这个示例中我们分别在常用配置数据的地方,CommandLine、环境变量、appsettings.json、ConfigureWebHostDefaults中的UseSetting和ConfigureAppConfiguration中读取自定义的文件mysettings.json中分别设置一个同名的配置节点叫FromSource,然后它的值设置FromSource节点的数据来自于哪个配置方式,比如环境变量中我配置的是Environment

"MyDemo.Config": {"commandName": "Project","launchBrowser": true,"applicationUrl": "http://localhost:19573","environmentVariables": {"ASPNETCORE_ENVIRONMENT": "Development","FromSource": "Environment"}

配置文件中我配置的是appsetting.json

{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","FromSource": "appsetting.json"
}

自定义的配置文件中我配置的是mysettings.json

{"FromSource": "mysetting.json"
}

然后在启动程序Program.cs中配置如下

public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureAppConfiguration(config => {config.AddJsonFile("mysettings.json", optional: true, reloadOnChange: true);}).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseSetting("FromSource", "UseSetting");webBuilder.UseStartup<Startup>();});

为了方便演示我们在程序的默认终结点中添加响应的读取代码

app.UseEndpoints(endpoints =>
{endpoints.MapGet("/", async context =>{await context.Response.WriteAsync($"Read Node FromSource={Configration["FromSource"]}");});
});

以上操作我们都完成了配置后,然后通过CLI的方式启动程序并传递--FromSource=CommandLine

dotnet run --FromSource=CommandLine

程序运行起来之后输入host+port的形式请求默认路径得到的结果是

Read Node FromSource=mysetting.json

说明默认情况下优先级最高的是通过ConfigureAppConfiguration方法注册自定义配置,然后我们注释掉设置读取mysetting.json数据源的相关代码,然后继续运行程序,得到的结果是

Read Node FromSource=CommandLine

这个是通过CLI启动程序我们手动传递的命令行参数,然后我们退出程序,再次通过CLI的方式运行程序,但是这次我们不传递--FromSource=CommandLine,得到的结果是

Read Node FromSource=Environment

这是我们在环境变量中配置的节点数据,然后我们注释掉在环境变量中配置的节点数据,再次启动程序得到的结果是

Read Node FromSource=appsetting.json

也就是我们在默认配置文件中appsetting.json配置的数据,然后我们注释掉这个数据节点,继续运行程序,毫无疑问得到的结果是

Read Node FromSource=UseSetting

通过这个演示结果我们可以得到这么一个结论,在Asp.Net Core中如果你采用的是系统默认的形式构建的程序,那么读取配置节点的优先级是ConfigureAppConfiguration(自定义读取)>CommandLine(命令行参数)>Environment(环境变量)>appsetting.json(默认配置文件)>UseSetting的顺序。

源码探究

要想知道,为什么演示示例会出现那种顺序,还要从源码着手。在之前的.Net Core Configuration源码探究中我们提到过Configuration读取数据的顺序采用的是后来者居上的形式,也就是说,后被注册的ConfigurationProvider中的数据会优先被读取到,这个操作处理在ConfigurationRoot类中可以找到相关逻辑[点击查看源码????],它的实现是这样的

public string this[string key]
{get{//通过这个我们可以了解到读取的顺序取决于注册Source的顺序,采用的是后来者居上的方式//后注册的会先被读取到,如果读取到直接returnfor (var i = _providers.Count - 1; i >= 0; i--){var provider = _providers[i];if (provider.TryGet(key, out var value)){return value;}}return null;}set{if (!_providers.Any()){throw new InvalidOperationException(Resources.Error_NoSources);}//这里的设置只是把值放到内存中去,并不会持久化到相关数据源foreach (var provider in _providers){provider.Set(key, value);}}
}

通过这段代码我们就心理就有底了,也就是说,上面示例表现出来的现象,无非就是注册顺序的问题。

默认的CreateDefaultBuilder

默认情况下我们都是通过Host.CreateDefaultBuilder(args)的方式去构建的HostBuilder,那么我们就从这个方法入手,找到源码位置????,我们抽离出关于配置操作的逻辑,大致如下

public static IHostBuilder CreateDefaultBuilder(string[] args)
{var builder = new HostBuilder();//配置默认内容根目录为当前程序运行目录builder.UseContentRoot(Directory.GetCurrentDirectory());//配置HostConfiguration,这个地方不要被吓到,最终通过HostConfiguration配置的操作都是要加载到ConfigureAppConfiguration里的//至于如何加载,待会我们会通过源码看到builder.ConfigureHostConfiguration(config =>{//先配置环境变量config.AddEnvironmentVariables(prefix: "DOTNET_");//然后配置命令行读取if (args != null){config.AddCommandLine(args);}});builder.ConfigureAppConfiguration((hostingContext, config) =>{var env = hostingContext.HostingEnvironment;//首先添加的就是读取appsettings.json相关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();//启动时命令行参数不为null则添加CommandLine读取if (args != null){config.AddCommandLine(args);}})//*其他部分逻辑已省略,有兴趣可自行点击上方连接查看源码return builder;
}

通过CreateDefaultBuilder我们可以非常清晰的得到这个结论由于先注册的是读取appsettings.json相关的逻辑,然后是AddEnvironmentVariables去读取环境变量,最后是AddCommandLine读取命令行参数加载到Configuration中,所以通过这个我们验证了优先级CommandLine(命令行参数)>Environment(环境变量)>appsetting.json(默认配置文件)的顺序。

ConfigureAppConfiguration中寻找答案

通过上面CreateDefaultBuilder我们得到了Configuration默认读取优先级的一部分逻辑认证,但是在示例的演示中,我们清楚的看到ConfigureAppConfiguration中配置的读取优先级是大于以上任何一个读取方式的,所以接下来我们还得需要到ConfigureAppConfiguration方法中一探究竟,这是一个扩展方法,默认调用的是HostBuilder中的ConfigureAppConfiguration方法[点击查看源码????]

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

_configureAppConfigActions是HostBuilder的私有属性

private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();

也就是说我们通过ConfigureAppConfiguration实现的逻辑都会被添加到_configureAppConfigActions这个List中,但是这个还不是我们要查找的核心。看来我们要去HostBuilder.Build()方法找寻找答案了,毕竟真正的构建逻辑还是在Build方法中,最后我们找到了如下方法[点击查看源码????]

private void BuildAppConfiguration()
{//用默认的ContentRootPath去构建一个全局的ConfigurationBuildervar configBuilder = new ConfigurationBuilder().SetBasePath(_hostingEnvironment.ContentRootPath)//首先就是把通过ConfigureHostConfiguration配置的相关添加到ConfigurationBuilder中.AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);//通过循环的方式去执行我们注册到_configureAppConfigActions集合中的逻辑foreach (var buildAction in _configureAppConfigActions){buildAction(_hostBuilderContext, configBuilder);}_appConfiguration = configBuilder.Build();_hostBuilderContext.Configuration = _appConfiguration;
}

由于_configureAppConfigActions是被循环执行的,也就是说先被注册到ConfigureAppConfiguration中的逻辑也是优先被执行,那么我们在CreateDefaultBuilder方法中,系统默认给我注册的AddJsonFile、AddEnvironmentVariables、AddCommandLine的调用顺序要优先于我们自行通过ConfigureAppConfiguration注册配置的逻辑。由于Configuration读取数据的顺序采用的是后来者居上的形式,所以我们自行通过ConfigureAppConfiguration注册的配置逻辑优先级是大于系统默认给我们注册读取配置的优先级。因此通过这些我们可以得到了这个结论ConfigureAppConfiguration(自定义读取)>CommandLine(命令行参数)>Environment(环境变量)>appsetting.json(默认配置文件)。除此之外还可以得到一个结论,默认情况下通过ConfigureHostConfiguration添加的配置相关,优先级是最低的。因为在循环执行_configureAppConfigActions循环之前,也就是在构建ConfigurationBuilder的时候就添加了ConfigureHostConfiguration。

UseSetting最后的迷雾

通过上面的相关源码我们已经得到了,关于默认配置读取优先级的大部分实现逻辑,仅仅剩下通过ConfigureWebHostDefaults中添加的UseSetting相关逻辑。可能有许多同学不清楚,其实UseSetting也是添加到配置系统当中去的,这个可以查看具体源码[点击查看源码????]

private IConfiguration _config = new ConfigurationBuilder().AddEnvironmentVariables(prefix: "ASPNETCORE_").Build();
public IWebHostBuilder UseSetting(string key, string value)
{_config[key] = value;return this;
}

也就是说,接下来我们只要找到_config是如何注册到全局的ConfigurationBuilder中,就能拨开最后的迷雾,找到真正的答案。我们通过入口方法ConfigureWebHostDefaults往下找,虽然过程有点曲折,但是我们还是在GenericWebHostBuilder的构造函数中找到了如下逻辑逻辑[点击查看源码????]

public GenericWebHostBuilder(IHostBuilder builder)
{_builder = builder;//这个就是上面UseSetting操作的_config_config = new ConfigurationBuilder().AddEnvironmentVariables(prefix: "ASPNETCORE_").Build();//把_config通过ConfigureHostConfiguration方法注册到了全局的ConfigurationBuilder中去_builder.ConfigureHostConfiguration(config =>{config.AddConfiguration(_config);ExecuteHostingStartups();});//*其他部分代码省略
}

看到这个逻辑突然就恍然大悟了,我们上面曾经说过通过ConfigureHostConfiguration添加的配置相关,优先级是最低的。因为在HostBuilder.Build()调用的BuildAppConfiguration方法中我们可以得知,在循环执行_configureAppConfigActions循环之前,也就是在构建ConfigurationBuilder的时候就添加了ConfigureHostConfiguration。而UseSetting操作的Configuration正是通过ConfigureHostConfiguration注册到ConfigurationBuilder中去的,因此通过UseSetting添加的配置相关优先级要低于之前我们提到的其他配置逻辑。

总结

通过本次谈到我们得到了默认情况下读取配置Configuration的默认优先级,也就是ConfigureAppConfiguration(自定义读取)>CommandLine(命令行参数)>Environment(环境变量)>appsetting.json(默认配置文件)>UseSetting的顺序。然后我们通过分析源码的形式,得到了为什么会是这个读取优先级的缘由。总之还是脱离不了那个宗旨,Configuration读取数据的顺序采用的是后来者居上的形式,后被注册的会优先被读取到。
    说点题外话,我觉得阅读源码是一件非常有趣的事情,不是说我要把所有源码看一遍,或者都能看懂。而是当我心理产生了疑惑,但是这个疑惑我通过阅读源码的途径变得豁然开朗,这才是读源码真正的乐趣所在。漫无目的或者为了读而读,会失去兴趣所在,容易导致效率低下,看明白了源码的设计,提升了自己的思维方式,也许才是真正的自我提升。

????欢迎扫码关注我的公众号????

深入探究.Net Core Configuration读取配置的优先级相关推荐

  1. 使用Apache Commons Configuration读取配置信息

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中使用一些比较新的库总会给你带来很多快乐,在这篇文章中,我将会给你介绍一个在Java中读取配置文件的框架--Apache ...

  2. JavaWEB中读取配置信息

    第一种方法是使用java.io和java.util包,缺点是路径的概念要清晰, 例子: Properties prop = new Properties(); InputStream in = get ...

  3. .Net Core 自定义配置源从配置中心读取配置

    配置,几乎所有的应用程序都离不开它..Net Framework时代我们使用App.config.Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再 ...

  4. ASP.NET Core的配置(1):读取配置信息

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  5. .Net core下的配置设置(一)——Configuration

    ASP.NET Core 中提供了一个Configuration 包,用以应用配置基于配置提供程序建立的键值对.这里以json文件配置的方式,简单的介绍一下它的用法. 首先定义一个配置文件appset ...

  6. asp 禁止某一个目录_asp.net core 系列 10 配置configuration (上)

    一. ASP.NET Core 中的配置概述 ASP.NET Core 中的应用配置是基于键值对,由configuration 程序提供. configuration 将从各种配置源提供程序操作键值对 ...

  7. 翻译 - ASP.NET Core 基本知识 - 配置(Configuration)

    翻译自 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0 ASP ...

  8. 如何使用AWS和Azure的配置存储服务保存读取配置

    原文:Want to yank configuration values from your .NET Core apps? 作者:pauljwheeler 译文:https://www.cnblog ...

  9. .NET Core开发日志——配置

    .NET Core开发日志--配置 原文:.NET Core开发日志--配置 熟悉ASP.NET的开发者一定对web.config文件不陌生.在ASP.NET环境中,要想添加配置参数,一般也都会在此文 ...

最新文章

  1. 演讲十忌(翻译并制作成PPT)
  2. volatile的作用(转自于http://www.csdn.net/)——今天差点忘记了!
  3. 电脑开机一会就蓝屏怎么回事_客户电脑老是出现问题,三天来找三次麻烦!拆机后“真凶”大白!...
  4. 环境变量_UG环境变量设置
  5. 三极管驱动(讲的比较好)
  6. 代码里-3gt;gt;1是-2但3gt;gt;1是1,-3/2却又是-1,为什么?
  7. 【Servlet】Cookie会话跟踪技术
  8. 华为笔试题-磁盘容量排序
  9. 关于codeMirror插件使用的一个坑
  10. Process Explorer 诊断和排错实例(下)
  11. 解决 https 证书验证不通过的问题
  12. MFC在指定控件区域内进行一些操作
  13. 闲置eSATA接口影响Windows7启动速度案例分析
  14. 谷歌浏览器:下载,插件安装
  15. 组合查询用例-正交设计法如何设计
  16. 用好这 43 款 Chrome 插件,让你开发学习一下子好轻松!
  17. SQL基础知识归纳总结
  18. 泉州有了一个水务大脑,用AI守护“绿水青山”
  19. macOS Big Sur 11.2.2 (20D80) 虚拟机 ISO 镜像
  20. 【网络--实验】华三防火墙命令行调试实例

热门文章

  1. 构建和实现单点登录解决方案(转载于IBMdeveloperWorks)
  2. (第九周)团队项目14
  3. Public DNS (公共域名解析服务)
  4. poj3685 二分套二分
  5. C# 内存法图像处理
  6. 一个设置ip的vbs脚本
  7. sci-hub谷歌插件_Google Home Hub具有隐藏屏幕设置菜单
  8. 微信小程序黑客马拉松即将开始,来做最酷的 Mini Program Creators!
  9. 10 个有关 String 的面试问题
  10. PHP会话控制考察点