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 ...
随机推荐
- web应用和虚拟目录映射
Tip:WEB应用程序 WEB应用程序指供浏览器访问的程序,通常简称为web应用. 一个web应用由多个静态web资源和动态web资源组成,如: HTML.css.js文件 JSP文件.java程序. ...
- lua 字符串 正则表达式 转义 特殊字符
string.gsub 函数有三个参数:目标串,模式串,替换串.基本作用是用来查找匹配模式的串,并将使用替换串其替换掉: s = string.gsub("Lua is good" ...
- JAVA使用SAX解析XML文件
在我的另一篇文章(http://www.cnblogs.com/anivia/p/5849712.html)中,通过一个例子介绍了使用DOM来解析XML文件,那么本篇文章通过相同的XML文件介绍如何使 ...
- ViewController的生命周期
# ViewController 的生命周期 # ViewController的生命周期中各个方法的流程如下: init loadView :加载view viewDidLoad :view加载完毕 ...
- CSS列表逆序
要使列表逆序的话,大多数人包括我一半都会选择在ol标签里使用reversed属性 <ol reversed> <li>first</li> <li>se ...
- Android :fragment介绍
一.关于Fragmemt 1.Fragment(片段),主要是为了支持更多的动态和灵活的用户界面设计,如平板电脑.Fragment允许组合和交换用户界面组件,而不需要更改视图层次结构.通过把Activ ...
- C++中的显式类型转化
类型转化也许大家并不陌生,int i; float j; j = (float)i; i = (int)j; 像这样的显式转化其实很常见,强制类型转换可能会丢失部分数据,所以如果不加(int)做强制转 ...
- FAT32 FAT区__FAT表解析
一. FAT 表概述 位置: 紧跟在文件系统的“保留区”之后 : 有两个数据结构完全相同的FAT(FAT,File Allocation Tbale 文件分配表)组成. 作用: FAT表项,描述文件系 ...
- 关于OpenStack的学习路线及相关资源汇总
首先我们想学习openstack,那么openstack是什么?能干什么?涉及的初衷是什么?由什么来组成?刚接触openstack,说openstack不是一个软件,而是由多个组件进行组合,这是一个更 ...
- Python 的简单图形界面编程【草】
可用方案 Tkinter python官方附带,方便,但听说存在乱码问题 wxPython 更成熟一些,但需要额外安装(大约50M) pyQt 授权不够宽松 最短代码 Tkinter 待补充 wxPy ...