ASP.NET Core的配置(4):多样性的配置来源[上篇]
较之传统通过App.config和Web.config这两个XML文件承载的配置系统,ASP.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持。我们可以将内存变量、命令行参数、环境变量和物理文件作为原始配置数据的来源,如果采用物理文件作为配置源,我们可以选择不同的格式,比如XML、JSON和INI等。如果这些默认支持的配置源形式还不能满足你的需求,我们还可以通过注册自定义ConfigurationProvider的方式将其他形式数据作为我们的配置来源。接下来就让我们来逐个认识一下配置模型原生提供的ConfigurationProvider。
目录
MemoryConfigurationProvider
EnvironmentVariablesConfigurationProvider
CommandLineConfigurationProvider
JsonConfigurationProvider
XmlConfiguationProvider
IniConfigurationProvider
自定义ConfigurationProvider
一、MemoryConfigurationProvider
通过本章第2节对配置模型的介绍,我们知道ConfigurationProvider在配置模型中所起的作用就是读取原始的配置数据并将其转换成基于数据字典的物理结构。在所有的ConfigurationProvider类型中,MemoryConfigurationProvider最为简单直接,因为它对应的配置源就是一个数据字典,根本不需要作任何的结构转换。
MemoryConfigurationProvider定义在“Microsoft.Extensions.Configuration.Memory”命名空间下。。如下面的代码片段所示,派生于基类ConfigurationProvider的MemoryConfigurationProvider同时实现了IEnumerable<KeyValuePair<string, string>>接口,所以它自身可以作为一个字典对象来使用。原始的配置数据可以在创建MemoryConfigurationProvider的时候作为构造函数的参数来指定,也可以通过调用Add方法逐个进行添加。
- 1: public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable<KeyValuePair<string, string>>
- 2: {
- 3: public MemoryConfigurationProvider();
- 4: public MemoryConfigurationProvider(IEnumerable<KeyValuePair<string, string>> initialData);
- 5:
- 6: public void Add(string key, string value);
- 7: public IEnumerator<KeyValuePair<string, string>> GetEnumerator();
- 8: }
在使用的时候,我们需要将MemoryConfigurationProvider对象注册到ConfigurationBuilder之上。具体来说,我们可以像前面演示的实例一样直接调用ConfigurationBuilder的Add方法,也可以调用如下所示的扩展方法AddInMemoryCollection。
- 1: public static class MemoryConfigurationExtensions
- 2: {
- 3: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder);
- 4: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder, IEnumerable<KeyValuePair<string, string>> initialData);
- 5: }
二、EnvironmentVariablesConfigurationProvider
顾名思义,环境变量就是描述当前执行环境并影响进程执行行为的变量。按照作用域的不同,我们将环境变量非常三类,它们分别针对当前系统、当前用户和当前进程。系统和用户级别的环境变量保存在注册表中,其路径分别为“HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment”和“HKEY_CURRENT_USER\Environment ”。
环境变量提取和维护可以通过静态类型Environment来实现。具体来说,我们可以调用静态方法GetEnvironmentVariable方法获得某个指定名称的环境变量的值,而GetEnvironmentVariables方法则会将返回所有的环境变量,EnvironmentVariableTarget枚举类型的参数代表环境变量作用域决定的存储位置。如果在调用GetEnvironmentVariable或者GetEnvironmentVariables方法师没有显式指定target参数或者将此参数指定为EnvironmentVariableTarget.Process,在进程初始化前存在的所有环境变量(包括针对系统、当前用户和当前进程)将会作为候选列表。
- 1: public static class Environment
- 2: {
- 3: public static string GetEnvironmentVariable(string variable);
- 4: public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target);
- 5: public static IDictionary GetEnvironmentVariables();
- 6: public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target);
- 7:
- 8: public static void SetEnvironmentVariable(string variable, string value);
- 9: public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target);
- 10: }
- 11:
- 12: public enum EnvironmentVariableTarget
- 13: {
- 14: Process,
- 15: User,
- 16: Machine
- 17: }
环境变量的添加、修改和删除均由SetEnvironmentVariable方法来实现,如果没有显式指定target参数,默认采用的是EnvironmentVariableTarget.Process。如果希望删除指定名称的环境变量,我们只需要在调用这个方法的时候将value参数设置为Null或者空字符串即可。
借助EnvironmentVariablesConfigurationProvider,我们可以将环境变量作为配置源。该类型定义在“Microsoft.Extensions.Configuration.EnvironmentVariables”程序集中,程序集的名称同时也是所在NuGet包的名称。如下面的代码片段所示,EnvironmentVariablesConfigurationProvider具有两个构造函数重载,如果调用默认无参构造函数,意味着我们会使用所有的环境变量。另一个构造函数提供了一个字符串类型的参数prefix,如果调用这个构造函数来创建一个EnvironmentVariablesConfigurationProvider,意味着我们只会使用名称以此为前缀的环境变量。
- 1: public class EnvironmentVariablesConfigurationProvider : ConfigurationProvider
- 2: {
- 3: public EnvironmentVariablesConfigurationProvider();
- 4: public EnvironmentVariablesConfigurationProvider(string prefix);
- 5: public override void Load();
- 6: }
由于作为原始配置数据的环境变量本身就是一个Key和Value均为字符串的数据字典,所以EnvironmentVariablesConfigurationProvider无需在进行结构转换,所以当Load方法被执行之后,它只需要将符合条件筛选出来并添加到自己的配置字典中即可。有一点值得一提的是,如果我们在创建EnvironmentVariablesConfigurationProvider对象是指定了用于筛选环境变量的前缀,当符合条件的环境变量被添加到自身的配置字典之后,这个前缀也会从元素的Key中剔除。如下所示的代码片段基本上体现了EnvironmentVariablesConfigurationProvider的实现逻辑。
- 1: public class EnvironmentVariablesConfigurationProvider : ConfigurationProvider
- 2: {
- 3: private readonly string prefix;
- 4:
- 5: public EnvironmentVariablesConfigurationProvider(string prefix = null)
- 6: {
- 7: this.prefix = prefix ?? string.Empty;
- 8: }
- 9:
- 10: public override void Load()
- 11: {
- 12: var dictionary = Environment.GetEnvironmentVariables()
- 13: .Cast<DictionaryEntry>()
- 14: .Where(it => it.Key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
- 15: .ToDictionary(it => it.Key.ToString().Substring(prefix.Length), it => it.Value.ToString());
- 16: this.Data = new Dictionary<string, string>(dictionary, StringComparer.OrdinalIgnoreCase);
- 17: }
- 18: }
也正是因为环境变量自身是数据字典,所以我们可以采用路径化的变量名定义一组相关的环境变量来提供一个复杂对象、集合或者字典对象的配置数据。如下面的代码片段所示,我们采用这样的方式将绑定为一个Profile对象的基本信息定义成一组相关的环境变量。由于这组环境变量名称具有相同的前缀“Profile”,所以我们利用这个前缀来创建一个 EnvironmentVariablesConfigurationProvider对象。在将它添加到ConfigurationBuilder之后,我们是用后者生成的Configuration对象采用配置绑定的方式得到一个Profile对象。
- 1: Environment.SetEnvironmentVariable("Profile:Gender", "Male");
- 2: Environment.SetEnvironmentVariable("Profile:Age", "18");
- 3: Environment.SetEnvironmentVariable("Profile:ContactInfo:Email", "foobar@outlook.com");
- 4: Environment.SetEnvironmentVariable("Profile:ContactInfo:PhoneNo", "123456789");
- 5:
- 6: Profile profile = new ConfigurationBuilder()
- 7: .Add(new EnvironmentVariablesConfigurationProvider("Profile:"))
- 8: .Build()
- 9: .Get<Profile>();
在使用EnvironmentVariablesConfigurationProvider的时候,我们可以按照上面演示的方式显式地调用Add方法将创建的EnvironmentVariablesConfigurationProvider对象注册到指定的ConfigurationBuilder对象之外,也可以直接调用如下所示的扩展方法AddEnvironmentVariables。
- 1: public static class EnvironmentVariablesExtensions
- 2: {
- 3: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder);
- 4: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder, string prefix);
- 5: }
三、CommandLineConfigurationProvider
在很多情况下,我们会采用Self-Host的方式将一个ASP.NET Core应用寄宿一个托管进程中,在这种情况下我们倾向于采用命令行的方式来启动寄宿程序。当以命令行的形式启动一个ASP.NET Core应用时,我们希望直接使用命名行开关(Switch)来控制应用的一些行为,所以命令行开关自然也就成为了配置常用的来源之一。配置模型针对这种配置源的支持是通过CommandLineConfigurationProvider来实现的,该类型定义在“Microsoft.Extensions.Configuration.CommandLine”程序集中,这也是所在NuGet包的名称。
在以命令行的形式执行某个命令的时候,命令行开关(包括名称和值)体现为一个简单的字符串集合,所以CommandLineConfigurationProvider的根本目的在于将命名行开关从字符串集合的形式转换成配置字典的形式。要充分理解这个转换规则,我们先得来了解一下CommandLineConfigurationProvider支持的命令行开关究竟采用怎样的形式来指定。我们通过一个简单的实例来说明命令行开关的集中指定方式。假设我们有一个命令“exec”并采用如下所示的方式执行某个托管程序(app)。
- 1: exec app {options}
在执行这个命令的时候我们通过相应的命令行开关指定两个选项,其中一个表示采用的CPUI架构(X86或者X64),另一个表示运行时类型(CLR或者CoreCLR),我们将这两个命令行开关分别命名为architecture和runtime。在执行命名行的时候,我们可以采用如下三种不同的方式指定这两个命名行开关。
- 1: exec app /architecture x64 /runtime coreclr
- 2: exec app --architecture x64 --runtime coreclr
- 3: exec app architecture=x64 architecture=coreclr
为了执行上的便利,很多命名行开关都具有缩写的形式。以上述的这两个命令行开关为例,我们可以采用首字母“a”和“r”来代表作为全名的“architecture”和“runtime”。如果采用缩写的命令行开关名称,那么我们就可以按照如下两种方式指定CPU架构和运行时类型。
- 1: exec app –-a x64 –-r coreclr
- 2: exec app -a x64 -r coreclr
综上所示,我们一共有五种指定命名行开关的方式,其中三种采用命令行开关的全名,余下的两种则使用命令行开关的缩写形式。这五种命名开关的指定形式所采用的原始参数以及缩写与全名的映射关系。
在对命令行开关的集中指定形式具有基本了解之后,我们来认识一下将它们引入配置模型并作为配置源数据来源的CommandLineConfigurationProvider。如下面的代码片断所示,我们需要以字符串集合的形式指定原始的命令行参数来创建一个CommandLineConfigurationProvider对象,只读属性Args返回的也正是这个集合。
- 1: public class CommandLineConfigurationProvider : ConfigurationProvider
- 2: {
- 3: public CommandLineConfigurationProvider(IEnumerable<string> args, IDictionary<string, string> switchMappings = null);
- 4: public override void Load();
- 5:
- 6: protected IEnumerable<string> Args { get; }
- 7: }
构造函数另一个字典类型的参数switchMappings用于指定命令行开关名称的缩写形式与全名的映射关系。一个命令行开关可以包含多个不同的缩写形式,比如“architecture”可以缩写成“a”,也和缩写成“arch”。如果采用缩写形式,指定的命名行开关名称必须以“-”或者“--”为前缀,那么这个switchMappings参数对应字典对象中的Key也需要采用相应的前缀。
在使用CommandLineConfigurationProvider的时候,我们可以直接创建这个对象并调用Add方法将其添加到指定的ConfigurationBuilder之中。我们可以直接调用ConfigurationBuilder对象具有如下定义的两个扩展方法AddCommandLine达到相同的目的。
- 1: public static class CommandLineConfigurationExtensions
- 2: {
- 3: public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args);
- 4: public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args,IDictionary<string, string> switchMappings);
- 5: }
我们照例通过通过一个简单的实例来演示如何利用CommandLineConfigurationProvider将命令行开关作为配置的原始来源。如下面的代码片断所示,在静态方法GetConfigurations中,我们按照上面表格所示的五种方式创建了以命名行参数作为来源的Configuration对象。为了验证这五种命名行开关指定形式的等效性,我们从中提取配置项“architecture”和“runtime”并验证它们的值。
- 1: public class Program
- 2: {
- 3: public static void Main(string[] args)
- 4: {
- 5: foreach (IConfiguration configuration in GetConfigurations())
- 6: {
- 7: Debug.Assert(configuration["architecture"] == "x64");
- 8: Debug.Assert(configuration["runtime"] == "coreclr");
- 9: }
- 10: }
- 11:
- 12: private static IEnumerable<IConfiguration> GetConfigurations()
- 13: {
- 14: yield return new ConfigurationBuilder()
- 15: .AddCommandLine(new string[] { "/architecture", "x64", "/runtime", "coreclr" })
- 16: .Build();
- 17:
- 18: yield return new ConfigurationBuilder()
- 19: .AddCommandLine(new string[] { "--architecture", "x64", "--runtime", "coreclr" })
- 20: .Build();
- 21:
- 22: yield return new ConfigurationBuilder()
- 23: .AddCommandLine(new string[] { "architecture=x64", "runtime=coreclr" })
- 24: .Build();
- 25:
- 26: yield return new ConfigurationBuilder()
- 27: .AddCommandLine(new string[] { "--a", "x64", "--r", "coreclr" }, new Dictionary<string, string>
- 28: {
- 29: ["--a"] = "architecture",
- 30: ["--r"] = "runtime"
- 31: }).Build();
- 32:
- 33: yield return new ConfigurationBuilder()
- 34: .AddCommandLine(new string[] { "-a", "x64", "-r", "coreclr" }, new Dictionary<string, string>
- 35: {
- 36: ["-a"] = "architecture",
- 37: ["-r"] = "runtime"
- 38: }).Build();
- 39: }
- 40: }
考虑到命名行的使用场景,我们一般情况下只利用命令行开关来提供单一的配置项,很少将其邦定为一个结构化的Options对象。不过命名行开关虽然以字符串集合的形式体现,但是它们可以直接映射为配置字典,所以我们完全可以通过采用路径化的命令行开关(比如“/foo:bar:baz abc”)来提供最终绑定为复杂对象设置集合和字典的配置源。
ASP.NET Core的配置(1):读取配置信息
ASP.NET Core的配置(2):配置模型详解
ASP.NET Core的配置(3): 将配置绑定为对象[上篇]
ASP.NET Core的配置(3): 将配置绑定为对象[下篇]
ASP.NET Core的配置(4):多样性的配置源[上篇]
ASP.NET Core的配置(4):多样性的配置源[中篇]
ASP.NET Core的配置(4):多样性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]
ASP.NET Core的配置(4):多样性的配置来源[上篇]的更多相关文章
- ASP.NET Core 2.2 基础知识(六) 配置(内含MySql+EF)
先上一段代码,了解一下 .NET Core 配置数据的结构. 新建一个 控制台项目,添加一个文件 json.json ,文件内容如下: { "country": "cn& ...
- asp.net core 3.0 MVC JSON 全局配置
asp.net core 3.0 MVC JSON 全局配置 System.Text.Json(default) startup配置代码如下: using System.Text.Encodings. ...
- [ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]
依赖注入不仅是支撑整个ASP.NET Core框架的基石,也是开发ASP.NET Core应用采用的基本编程模式,所以依赖注入十分重要.依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式 ...
- [ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
四.直接初始化Options对象 前面演示的几个实例具有一个共同的特征,即都采用配置系统来提供绑定Options对象的原始数据,实际上,Options框架具有一个完全独立的模型,可以称为Options ...
- [ASP.NET Core开发实战]基础篇06 配置
配置,是应用程序很重要的组成部分,常常用于提供信息,像第三方应用登录钥匙.上传格式与大小限制等等. ASP.NET Core提供一系列配置提供程序读取配置文件或配置项信息. ASP.NET Core项 ...
- [ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]
微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的 ...
- [转][ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]
微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的 ...
- ASP.NET Core 缓存技术 及 Nginx 缓存配置
前言 在Asp.Net Core Nginx部署一文中,主要是讲述的如何利用Nginx来实现应用程序的部署,使用Nginx来部署主要有两大好处,第一是利用Nginx的负载均衡功能,第二是使用Nginx ...
- Asp.Net Core轻松入门之WebHost的配置
在本篇文章中,我来讲一讲如何利用WebHost来加载配置文件和设置启动的Url 在前面的文章中讲过,ASP.Net Core应用程序会自动加载appsettings.json中的配置文件,那么如果配置 ...
- ASP.NET Core 从 gitlab-ci 环境变量读取配置
最近在加强持续集成,遇到一个场景需要通过 gitlab-ci 环境变量(Settings -> Settings -> CI/CD -> Variables )在持续集成时向 ASP ...
随机推荐
- Android RecyclerView 的简单使用
Android L SDK发布的,新API中最有意思的就是RecyclerView (后面为RV) 和 CardView了, 按照官方的说法, RV 是一个ListView 的一个更高级更灵活的一个版 ...
- JSON数据和对象
在js中像数字类型.字符串类型.布尔类型这些都不能再被拆分,属于基本类型.与之相对有一种复杂类型:对象类型,它是本身由多个其他类型组合而成的. 创建对象有两种方法,一.new Object()创建一个 ...
- 【Telerik】弹出对话框RadWindow,确认删除信息
要做一个删除功能,但是删除前正常都要弹出对话框确认一下是否删除信息,防止误删信息.
- common.js js中常用方法
//创建CSS样式段 //classid: CSS样式段ID//font: 字体//size: 字体大小//color: 字体颜色//style: 字体风格function FCMakeCSSClas ...
- javascript 原型及原型链的初步理解
最近折腾了好久,终于是把js里面的原型和原型链做了个初步的理解: 在这里,我打个比喻: 我(child),我妈constructor(构造函数)生了我:别人问我老妈跟谁生的我,于是此时我妈会指向我爸爸 ...
- python奇偶数求和
#求100内奇数和while\for..in循环 sum = 0 i = 1 while i <= 100: sum += i i += 2 print(sum) sum = 0 for i i ...
- Win7网上邻居提示未授予用户在此计算机上的请求登录类型解决办法
内容简介 装了Win7之后很多人遇到这样的问题,网上邻居访问Win7的电脑时出现“未授予用户在此计算机上的请求登录类型”问题.打开“控制面板”--“管理工具”--“本地安全策略”--“本地策略 ...
- js面向对象基础总结
js中如何定义一个类? 定义的function就是一个构造方法也就是说是定义了一个类:用这个方法可以new新对象出来. function Person(name, age){ this.name = ...
- SQL Server 2016中In-Memory OLTP继CTP3之后的新改进
SQL Server 2016中In-Memory OLTP继CTP3之后的新改进 转译自:https://blogs.msdn.microsoft.com/sqlserverstorageengin ...
- 安卓动态调试七种武器之孔雀翎 – Ida Pro
安卓动态调试七种武器之孔雀翎 – Ida Pro 作者:蒸米@阿里聚安全 0x00 序 随着移动安全越来越火,各种调试工具也都层出不穷,但因为环境和需求的不同,并没有工具是万能的.另外工具是死的,人是 ...