较之传统通过App.config和Web.config这两个XML文件承载的配置系统,.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持。我们可以将内存变量、命令行参数、环境变量和物理文件作为原始配置数据的来源,如果采用物理文件作为配置源,我们可以选择不同的格式(比如XML、JSON和INI等) 。如果这些默认支持的配置源形式还不能满足你的需求,我们还可以通过注册自定义ConfigurationSource的方式将其他形式数据作为我们的配置来源。 [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、内存变量
二、环境变量
三、命令行参数

一、内存变量

从本被系列第一篇开始到现在,我们所有的实例演示一直都在使用MemoryConfigurationSource这种类型的ConfigurationSource来提供原始的配置。我们知道MemoryConfigurationSource采用一个字典对象(具体来说应该是一个元素类型为KeyValuePair<string, string>的集合)作为存放原始配置数据的容器。作为一个ConfigurationSource,它总是通过创建某个对应的ConfigurationProvider来从事具体的配置数据读取工作,那么MemoryConfigurationSource会提供一个怎样的ConfigurationProvider呢?

   1: public class MemoryConfigurationSource : IConfigurationSource

   2: {

   3:     public IEnumerable<KeyValuePair<string, string>> InitialData { get; set; }

   4:  

   5:     public IConfigurationProvider Build(IConfigurationBuilder builder)

   6:     {

   7:         return new MemoryConfigurationProvider(this);

   8:     }

   9: }

上面给出的代码片段体现了MemoryConfigurationSource的完整定义,我们可以看到它具有一个IEnumerable<KeyValuePair<string, string>>类型的属性InitialData来存放初始的配置数据。从Build方法的实现可以看出,真正被它用来读取原始配置数据的是一个MemoryConfigurationProvider类型的对象,该类型的定义如下面的代码片段所示。

   1: public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable<KeyValuePair<string, string>>

   2: {

   3:     public MemoryConfigurationProvider(MemoryConfigurationSource source);

   4:     public void Add(string key, string value);

   5:     public IEnumerator<KeyValuePair<string, string>> GetEnumerator();

   6:     IEnumerator IEnumerable.GetEnumerator();

   7: }

从上面的代码片段可以看出,MemoryConfigurationProvider派生于抽象类ConfigurationProvider,同时还实现了IEnumerable<KeyValuePair<string, string>>接口。我们知道ConfigurationProvider直接使用一个Dictionary<string, string>来保存配置数据,当我们根据一个MemoryConfigurationSource对象调用构造函数创建MemoryConfigurationProvider的时候,它只需要将通过InitiateData属性保存的配置数据转移到这个字典中即可。MemoryConfigurationProvider还定义了一个Add方法是我们可以在任何时候都可以向配置字典中添加一个新的配置项。

通过前面对配置模型的介绍,我们知道ConfigurationProvider在配置模型中所起的作用就是读取原始的配置数据并将其转换成配置字典。在所有的预定义的ConfigurationProvider类型中,MemoryConfigurationProvider最为简单直接,因为它对应的配置源就是一个配置字典,所以根本不需要作任何的结构转换。

在利用MemoryConfigurationSource生成配置的时候,我们需要将它注册到ConfigurationBuilder之上。具体来说,我们可以像前面演示的实例一样直接调用ConfigurationBuilder的Add方法,也可以调用如下所示的了两个重载的扩展方法AddInMemoryCollection。

   1: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder);

   2: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder, IEnumerable<KeyValuePair<string, string>> initialData);

二、环境变量

顾名思义,环境变量就是描述当前执行环境并影响进程执行行为的变量。按照作用域的不同,我们将环境变量划分成三类,即分别针对当前系统、当前用户和当前进程的环境变量。系统和用户级别的环境变量保存在注册表中,其路径分别为“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:  

   6:     public static IDictionary GetEnvironmentVariables();

   7:     public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target);

   8:  

   9:     public static void SetEnvironmentVariable(string variable, string value);

  10:     public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target);

  11: }

  12:  

  13: public enum EnvironmentVariableTarget

  14: {

  15:     Process,

  16:     User,

  17:     Machine

  18: }

环境变量的添加、修改和删除均由SetEnvironmentVariable方法来完成,如果没有显式指定target参数,默认采用的是EnvironmentVariableTarget.Process。如果希望删除指定名称的环境变量,我们只需要在调用这个方法的时候将value参数设置为Null或者空字符串即可。

除了在程序中利用静态类型Environment,我们还可以执行命令行的方式查看和设置环境变量。除此之外,我们还可以利用“系统属性(System Properties)”设置工具以可视化的方式查看和设置系统和用户级别的环境变量(“This PC”>“Properties”>“Change Settings”>“Advanced”>“Environment Variables”)。如果采用Visual Studio 2015来调试我们编写的应用,我们可以设置项目属性的方式来设置进程级别的环境变量( “Properties” > “Debug”> “Environment Variables” )。

针对环境变量的配置源通过如下一个 EnvironmentVariablesConfigurationSource类型来表示,该类型定义在NuGet包“Microsoft.Extensions.Configuration.EnvironmentVariables”之中。该类型指定义了一个字符串类型的属性Prefix,它表示用于筛选环境变量采用的前缀,也就是说如果我们设置了这个Prefix属性,只会选择名称以此作为前缀的环境变量。

   1: public class EnvironmentVariablesConfigurationSource : IConfigurationSource

   2: {

   3:     public string Prefix { get; set; }

   4:     public IConfigurationProvider Build(IConfigurationBuilder builder)

   5:     {

   6:         return new EnvironmentVariablesConfigurationProvider(this.Prefix);

   7:     }

   8: }

通过上面给出的代码片段我们可以看出EnvironmentVariablesConfigurationSource会利用对应的EnvironmentVariablesConfigurationProvider来完成对环境变量的读取工作。如下所示的代码基本体现了EnvironmentVariablesConfigurationProvider的定义。由于作为原始配置数据的环境变量本身就是一个Key和Value均为字符串的数据字典,所以EnvironmentVariablesConfigurationProvider无需在进行结构转换,所以当Load方法被执行之后,它只需要将符合条件筛选出来并添加到自己的配置字典中即可。值得一提的是,如果我们在创建EnvironmentVariablesConfigurationProvider对象是指定了用于筛选环境变量的前缀,当符合条件的环境变量被添加到自身的配置字典之后,这个前缀也会从元素的Key中剔除。这个细节也体现在上面定义的Load方法中。

   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: }

在使用EnvironmentVariablesConfigurationSource的时候,我们可以调用Add方法将它注册到指定的ConfigurationBuilder对象上。除此之外,EnvironmentVariablesConfigurationSource的中注册还可以直接调用IConfigurationBuilder接口的如下两个重载的扩展方法AddEnvironmentVariables来完成。

   1: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder);

   2: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder, string prefix);

我们照例编写一个简单的实例来演示如何采用环境变量作为配置源。如下面的代码片段所示,我们调用Environment的静态方法SetEnvironment方法设置了四个环境变量,变量名称具有相同的前缀“TEST_”。我们调用方法AddEnvironmentVariables创建一个EnvironmentVariablesConfigurationSource对象并将其注册到创建的ConfigurationBuilder之上。调用AddEnvironmentVariables方法是我们将环境变量名称前缀“TEST_” 作为参数。后续的代码我们已经很熟悉了,即采用Options模式读取环境变量并绑定为一个Profile对象。

   1: Environment.SetEnvironmentVariable("TEST_gender", "Male");

   2: Environment.SetEnvironmentVariable("TEST_age", "18");

   3: Environment.SetEnvironmentVariable("TEST_contactInfo:emailAddress", "foobar@outlook.com");

   4: Environment.SetEnvironmentVariable("TEST_contactInfo:PhoneNo", "123456789");

   5:  

   6: IConfiguration config = new ConfigurationBuilder()

   7:     .AddEnvironmentVariables("TEST_")

   8:     .Build();

   9:  

  10: Profile profile = new ServiceCollection()

  11:     .AddOptions()

  12:     .Configure<Profile>(config)

  13:     .BuildServiceProvider()

  14:     .GetService<IOptions<Profile>>()

  15:     .Value;

三、命令行参数

在很多情况下,我们会采用Self-Host的方式将一个ASP.NET Core应用寄宿一个托管进程中,在这种情况下我们倾向于采用命令行的方式来启动寄宿程序。当以命令行的形式启动一个ASP.NET Core应用时,我们希望直接使用命名行开关(Switch)来控制应用的一些行为,所以命令行开关自然也就成为了配置常用的来源之一。配置模型针对这种配置源的支持是通过CommandLineConfigurationSource来实现的,该类型定义在NuGet包 “Microsoft.Extensions.Configuration.CommandLine”中。

在以命令行的形式执行某个命令的时候,命令行开关(包括名称和值)体现为一个简单的字符串集合,所以CommandLineConfigurationSource的根本目的在于将命名行开关从字符串数组转换成配置字典。要充分理解这个转换规则,我们先得来了解一下CommandLineConfigurationSource支持的命令行开关究竟采用怎样的形式来指定。我们通过一个简单的实例来说明命令行开关的集中指定方式。假设我们有一个命令“exec”并采用如下所示的方式执行某个托管程序(app)。

   1: exec app {options} 

在执行这个命令的时候我们通过相应的命令行开关指定两个选项,其中一个表示采用的CPU架构(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

为了执行上的便利,很多命名行开关都具有缩写的形式,命令行开关的全名和缩写之间具有一个映射关系(Switch Mapping)。以上述的这两个命令行开关为例,我们可以采用首字母“a”和“r”来代表作为全名的“architecture”和“runtime”。如果采用缩写的命令行开关名称,那么我们就可以按照如下两种方式指定CPU架构和运行时类型。

   1: exec app –-a x64 –-r coreclr

   2: exec app -a x64 -r coreclr

综上所示,我们一共有五种指定命名行开关的方式,其中三种采用命令行开关的全名,余下的两种则使用命令行开关的缩写形式。下表总结了这五种命名开关的指定形式所采用的原始参数以及缩写与全名的映射关系。这里隐藏着一个重要的细节,字符 “-” 只能以缩写的形式指定命令行开关的指,但是 “--” 则支持全称和缩写形式。

Arguments

Switch Mapping

/architecture x64 /runtime coreclr

-

--architecture x64 --runtime coreclr

-

architecture=x64 runtime=coreclr

-

--a x64 --r coreclr

--a: architecture, --r: runtime

-a x64 -r coreclr

-a: architecture, -r: runtime

原始的命令行参数总是体现为一个字符串数组, CommandLineConfigurationSource以字符串数组作为配置源,并利用对应的ConfigurationProvider将它转换成配置字典。如下面的代码片断所示,CommandLineConfigurationSource具有Args和SwitchMappings,前者正式代表承载着原始命令行参数的字符串数组,后者则保存了命令行开关的缩写与全称之间的映射关系。在实现的Build方法中,它根据这两个属性创建出一个CommandLineConfigurationProvider对象。

   1: public class CommandLineConfigurationSource : IConfigurationSource

   2: {

   3:     public IEnumerable<string>         Args { get; set; }

   4:     public IDictionary<string, string>     SwitchMappings { get; set; }

   5:  

   6:     public IConfigurationProvider Build(IConfigurationBuilder builder)

   7:     {

   8:         return new CommandLineConfigurationProvider(this.Args, this.SwitchMappings);

   9:     }   

  10: }

具有如下定义的CommandLineConfigurationProvider依然是抽象类ConfigurationProvider的继承者。它的目的很明确,就是对体现为字符串数组的原始命令行参数进行解析,并将解析出来参数名称和值添加到配置字典中 。这一切都是在重写的Load方法中完成的。

   1: public class CommandLineConfigurationProvider : ConfigurationProvider

   2: {

   3:     protected IEnumerable<string> Args { get; }

   4:     public CommandLineConfigurationProvider(IEnumerable<string> args, IDictionary<string, string> switchMappings = null);

   5:     public override void Load();

   6: }

在采用基于命令行参数作为配置源的时候,我们可以创建一个CommandLineConfigurationSource并将其注册到ConfigurationBuilder之上。我们也可以调用IConfigurationBuilder接口的如下两个扩展方法AddCommandLine将两个步骤合二为一。

   1: public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args);

   2: public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args, IDictionary<string, string> switchMappings);

为了让读者朋友们对CommandLineConfigurationProvider解析命令行参数采用的策略具有一个深刻的认识,我们来演示一个简单的实例。我们创建一个控制台应用,并添加针对 “Microsoft.Extensions.Configuration.CommandLine”这个NuGet包的依赖。在Main方法中,我们编写了如下一段简单的实例程序。

   1: while (true)

   2: {

   3:     try

   4:     {

   5:         Console.Write("Enter command line switches:");

   6:         string arguments = Console.ReadLine();

   7:         Dictionary<string, string> mapping = new Dictionary<string, string>

   8:         {

   9:             ["--a"]     = "architecture ",

  10:             ["-a"]     = "architecture ",

  11:             ["--r"]     = "runtime",

  12:             ["-r"]     = "runtime",

  13:         };

  14:         IConfiguration config = new ConfigurationBuilder()

  15:             .AddCommandLine(arguments.Split(' '), mapping)

  16:             .Build();

  17:  

  18:         foreach (var section in config.GetChildren())

  19:         {

  20:             Console.WriteLine($"{section.Key}: {section.Value}");

  21:         }

  22:     }

  23:     catch(Exception ex)

  24:     {

  25:         Console.WriteLine(ex.Message);

  26:     }

  27: }

如上面的代码片断所示,我们在一个无限循环中接收用户指定的命令行参数,并据此创建一个CommandLineConfigurationSource对象并将其注册到ConfigurationBuilder之上。我们在创建这个CommandLineConfigurationSource对象的时候,还指定一个表示命令行开关映射关系的字典。接下来我们利用这个ConfigurationBuilder生成一个Configuration对象,并将其所有子配置节的Key和Value打印出来。我们运行该程序后分别采用上述五种方式提供了命令行参数,根据如下所示的输出结果,会发现解析命令行参数生成的配置是完全等效的。

.NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]的更多相关文章

  1. .NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?

    配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个 ...

  2. .NET Core采用的全新配置系统[7]: 将配置保存在数据库中

    我们在<聊聊默认支持的各种配置源>和<深入了解三种针对文件(JSON.XML与INI)的配置源>对配置模型中默认提供的各种ConfigurationSource进行了深入详尽的 ...

  3. .NET Core采用的全新配置系统[2]: 配置模型设计详解

    在<.NET Core采用的全新配置系统[1]: 读取配置数据>中,我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.NET Core的这个全新的配 ...

  4. .NET Core采用的全新配置系统[3]: “Options模式”下的配置是如何绑定为Options对象

    配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是采用<.NET Core采用的全新配置系统[1]: 读取 ...

  5. .NET Core系列 : 1、.NET Core 环境搭建和命令行CLI入门

    2016年6月27日.NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布,社区里涌现了很多文章,我也计划写个系列文章,原因是.NET Core的入门门槛相当高, ...

  6. NET Core 环境搭建和命令行CLI入门

    NET Core 环境搭建和命令行CLI入门 2016年6月27日.NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布,社区里涌现了很多文章,我也计划写个系列文 ...

  7. NET Core 环境搭建和命令行CLI入门[转]

      NET Core 环境搭建和命令行CLI入门 时间:2016-07-06 01:48:19      阅读:258      评论:0      收藏:0      [点我收藏+]   标签: N ...

  8. 【Python】 配置解析ConfigParser &amp; 命令行参数解析optparser

    ConfigParser ConfigParser包装了配置文件的读取和写入,使得python程序可以更加轻松操作配置文件了.这里的配置文件是指.ini的那种文件,基本格式如下 [section_a] ...

  9. .NET Core采用的全新配置系统[1]: 读取配置数据

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

随机推荐

  1. chorme浏览器调试Android设备

    Android设备开启开发者模式,并打开USB调试: 接着在Android设备上运行项目 在chrome浏览器打开F12: 在Remote devices里即可调试页面. ! 一般需要FQ

  2. 导航效果css

    <!doctype html> <html> <head> <meta charset="utf-8" /> <style&g ...

  3. Nine-Patch图片

    Nine-Patch图片以.9.png结尾,用作背景图片时,可使背景随着内容拉伸(缩小)而拉伸(缩小). 如何将普通图片制作为Nine-Patch图片: 在Android sdk目录下有一个tools ...

  4. Java Hour 41 Maven ( 3 )

    有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 41.1 m2eclipse 简介 m2eclipse 是一款开源工具,为ec ...

  5. Java Axis2 1.6.3+JDK1.7.0_13+Tomcat7.0.65+eclipse搭建web service

    安装文件下载: jdk1.7.0_13 安装步骤参考文章:http://jingyan.baidu.com/article/6dad5075d1dc40a123e36ea3.html tomcat7. ...

  6. 函数(jquery)

    <script type="text/javascript"> function makeArray(arg1, arg2){    return [ this, ar ...

  7. mysql 导出数据报错: row must be in range 0-65535

    数据导出时,出现错误: 一脸懵逼,百度了下,是导出数量有格式有限制.一开始导出为excel表格式,后改为文本格式,不会报错.

  8. IBatis.Net 视频教程 原创教程

    IBatis.Net 视频教程 列文件:共21个 Ibatis.Net 第01课 了解 和下载.avi Ibatis.Net 第02课 搭建简单三层项目 引入Ibatis.avi ibatis.net ...

  9. 5 -- Hibernate的基本用法 --1 1 对象/关系数据库映射(ORM)

    ORM的全称是Object/Relation Mapping ,即对象/关系数据库映射.ORM可理解成一种规范,它概述了这类框架的基本特征:完成面向对象的编程语言到关系数据库的映射.当ORM框架完成映 ...

  10. flask + mysql写的简单监控系统

    这里以监控内存使用率为例,写的一个简单demo性程序,具体操作根据51reboot提供的教程写如下. 一.建库建表 创建falcon数据库: mysql> create database fal ...