.NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]...
較之傳統(tǒng)通過App.config和Web.config這兩個XML文件承載的配置系統(tǒng),.NET Core采用的這個全新的配置模型的最大一個優(yōu)勢就是針對多種不同配置源的支持。我們可以將內(nèi)存變量、命令行參數(shù)、環(huán)境變量和物理文件作為原始配置數(shù)據(jù)的來源,如果采用物理文件作為配置源,我們可以選擇不同的格式(比如XML、JSON和INI等) 。如果這些默認(rèn)支持的配置源形式還不能滿足你的需求,我們還可以通過注冊自定義ConfigurationSource的方式將其他形式數(shù)據(jù)作為我們的配置來源。 [ 本文已經(jīng)同步到《ASP.NET Core框架揭秘》之中]
目錄
一、內(nèi)存變量
二、環(huán)境變量
三、命令行參數(shù)
一、內(nèi)存變量
從本被系列第一篇開始到現(xiàn)在,我們所有的實(shí)例演示一直都在使用MemoryConfigurationSource這種類型的ConfigurationSource來提供原始的配置。我們知道MemoryConfigurationSource采用一個字典對象(具體來說應(yīng)該是一個元素類型為KeyValuePair<string, string>的集合)作為存放原始配置數(shù)據(jù)的容器。作為一個ConfigurationSource,它總是通過創(chuàng)建某個對應(yīng)的ConfigurationProvider來從事具體的配置數(shù)據(jù)讀取工作,那么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: }上面給出的代碼片段體現(xiàn)了MemoryConfigurationSource的完整定義,我們可以看到它具有一個IEnumerable<KeyValuePair<string, string>>類型的屬性InitialData來存放初始的配置數(shù)據(jù)。從Build方法的實(shí)現(xiàn)可以看出,真正被它用來讀取原始配置數(shù)據(jù)的是一個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,同時還實(shí)現(xiàn)了IEnumerable<KeyValuePair<string, string>>接口。我們知道ConfigurationProvider直接使用一個Dictionary<string, string>來保存配置數(shù)據(jù),當(dāng)我們根據(jù)一個MemoryConfigurationSource對象調(diào)用構(gòu)造函數(shù)創(chuàng)建MemoryConfigurationProvider的時候,它只需要將通過InitiateData屬性保存的配置數(shù)據(jù)轉(zhuǎn)移到這個字典中即可。MemoryConfigurationProvider還定義了一個Add方法是我們可以在任何時候都可以向配置字典中添加一個新的配置項(xiàng)。
通過前面對配置模型的介紹,我們知道ConfigurationProvider在配置模型中所起的作用就是讀取原始的配置數(shù)據(jù)并將其轉(zhuǎn)換成配置字典。在所有的預(yù)定義的ConfigurationProvider類型中,MemoryConfigurationProvider最為簡單直接,因?yàn)樗鼘?yīng)的配置源就是一個配置字典,所以根本不需要作任何的結(jié)構(gòu)轉(zhuǎn)換。
在利用MemoryConfigurationSource生成配置的時候,我們需要將它注冊到ConfigurationBuilder之上。具體來說,我們可以像前面演示的實(shí)例一樣直接調(diào)用ConfigurationBuilder的Add方法,也可以調(diào)用如下所示的了兩個重載的擴(kuò)展方法AddInMemoryCollection。
1: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder); 2: public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder, IEnumerable<KeyValuePair<string, string>> initialData);
二、環(huán)境變量
顧名思義,環(huán)境變量就是描述當(dāng)前執(zhí)行環(huán)境并影響進(jìn)程執(zhí)行行為的變量。按照作用域的不同,我們將環(huán)境變量劃分成三類,即分別針對當(dāng)前系統(tǒng)、當(dāng)前用戶和當(dāng)前進(jìn)程的環(huán)境變量。系統(tǒng)和用戶級別的環(huán)境變量保存在注冊表中,其路徑分別為“HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment”和“HKEY_CURRENT_USER\Environment ”。
環(huán)境變量提取和維護(hù)可以通過靜態(tài)類型Environment來實(shí)現(xiàn)。具體來說,我們可以調(diào)用它的靜態(tài)方法GetEnvironmentVariable方法獲得某個指定名稱的環(huán)境變量的值,而GetEnvironmentVariables方法則會將返回所有的環(huán)境變量,EnvironmentVariableTarget枚舉類型的參數(shù)代表環(huán)境變量作用域決定的存儲位置。如果在調(diào)用GetEnvironmentVariable或者GetEnvironmentVariables方法師沒有顯式指定target參數(shù)或者將參數(shù)指定為EnvironmentVariableTarget.Process,在進(jìn)程初始化前存在的所有環(huán)境變量(包括針對系統(tǒng)、當(dāng)前用戶和當(dāng)前進(jìn)程)將會作為候選列表。
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: }環(huán)境變量的添加、修改和刪除均由SetEnvironmentVariable方法來完成,如果沒有顯式指定target參數(shù),默認(rèn)采用的是EnvironmentVariableTarget.Process。如果希望刪除指定名稱的環(huán)境變量,我們只需要在調(diào)用這個方法的時候?qū)alue參數(shù)設(shè)置為Null或者空字符串即可。
除了在程序中利用靜態(tài)類型Environment,我們還可以執(zhí)行命令行的方式查看和設(shè)置環(huán)境變量。除此之外,我們還可以利用“系統(tǒng)屬性(System Properties)”設(shè)置工具以可視化的方式查看和設(shè)置系統(tǒng)和用戶級別的環(huán)境變量(“This PC”>“Properties”>“Change Settings”>“Advanced”>“Environment Variables”)。如果采用Visual Studio 2015來調(diào)試我們編寫的應(yīng)用,我們可以設(shè)置項(xiàng)目屬性的方式來設(shè)置進(jìn)程級別的環(huán)境變量( “Properties” > “Debug”> “Environment Variables” )。
針對環(huán)境變量的配置源通過如下一個 EnvironmentVariablesConfigurationSource類型來表示,該類型定義在NuGet包“Microsoft.Extensions.Configuration.EnvironmentVariables”之中。該類型指定義了一個字符串類型的屬性Prefix,它表示用于篩選環(huán)境變量采用的前綴,也就是說如果我們設(shè)置了這個Prefix屬性,只會選擇名稱以此作為前綴的環(huán)境變量。
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會利用對應(yīng)的EnvironmentVariablesConfigurationProvider來完成對環(huán)境變量的讀取工作。如下所示的代碼基本體現(xiàn)了EnvironmentVariablesConfigurationProvider的定義。由于作為原始配置數(shù)據(jù)的環(huán)境變量本身就是一個Key和Value均為字符串的數(shù)據(jù)字典,所以EnvironmentVariablesConfigurationProvider無需在進(jìn)行結(jié)構(gòu)轉(zhuǎn)換,所以當(dāng)Load方法被執(zhí)行之后,它只需要將符合條件篩選出來并添加到自己的配置字典中即可。值得一提的是,如果我們在創(chuàng)建EnvironmentVariablesConfigurationProvider對象是指定了用于篩選環(huán)境變量的前綴,當(dāng)符合條件的環(huán)境變量被添加到自身的配置字典之后,這個前綴也會從元素的Key中剔除。這個細(xì)節(jié)也體現(xiàn)在上面定義的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的時候,我們可以調(diào)用Add方法將它注冊到指定的ConfigurationBuilder對象上。除此之外,EnvironmentVariablesConfigurationSource的中注冊還可以直接調(diào)用IConfigurationBuilder接口的如下兩個重載的擴(kuò)展方法AddEnvironmentVariables來完成。
1: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder); 2: public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder, string prefix);我們照例編寫一個簡單的實(shí)例來演示如何采用環(huán)境變量作為配置源。如下面的代碼片段所示,我們調(diào)用Environment的靜態(tài)方法SetEnvironment方法設(shè)置了四個環(huán)境變量,變量名稱具有相同的前綴“TEST_”。我們調(diào)用方法AddEnvironmentVariables創(chuàng)建一個EnvironmentVariablesConfigurationSource對象并將其注冊到創(chuàng)建的ConfigurationBuilder之上。調(diào)用AddEnvironmentVariables方法是我們將環(huán)境變量名稱前綴“TEST_” 作為參數(shù)。后續(xù)的代碼我們已經(jīng)很熟悉了,即采用Options模式讀取環(huán)境變量并綁定為一個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;
三、命令行參數(shù)
在很多情況下,我們會采用Self-Host的方式將一個ASP.NET Core應(yīng)用寄宿一個托管進(jìn)程中,在這種情況下我們傾向于采用命令行的方式來啟動寄宿程序。當(dāng)以命令行的形式啟動一個ASP.NET Core應(yīng)用時,我們希望直接使用命名行開關(guān)(Switch)來控制應(yīng)用的一些行為,所以命令行開關(guān)自然也就成為了配置常用的來源之一。配置模型針對這種配置源的支持是通過CommandLineConfigurationSource來實(shí)現(xiàn)的,該類型定義在NuGet包 “Microsoft.Extensions.Configuration.CommandLine”中。
在以命令行的形式執(zhí)行某個命令的時候,命令行開關(guān)(包括名稱和值)體現(xiàn)為一個簡單的字符串集合,所以CommandLineConfigurationSource的根本目的在于將命名行開關(guān)從字符串?dāng)?shù)組轉(zhuǎn)換成配置字典。要充分理解這個轉(zhuǎn)換規(guī)則,我們先得來了解一下CommandLineConfigurationSource支持的命令行開關(guān)究竟采用怎樣的形式來指定。我們通過一個簡單的實(shí)例來說明命令行開關(guān)的集中指定方式。假設(shè)我們有一個命令“exec”并采用如下所示的方式執(zhí)行某個托管程序(app)。
1: exec app {options}在執(zhí)行這個命令的時候我們通過相應(yīng)的命令行開關(guān)指定兩個選項(xiàng),其中一個表示采用的CPU架構(gòu)(X86或者X64),另一個表示運(yùn)行時類型(CLR或者CoreCLR),我們將這兩個命令行開關(guān)分別命名為architecture和runtime。在執(zhí)行命名行的時候,我們可以采用如下三種不同的方式指定這兩個命名行開關(guān)。
1: exec app /architecture x64 /runtime coreclr 2: exec app --architecture x64 --runtime coreclr 3: exec app architecture=x64 architecture=coreclr為了執(zhí)行上的便利,很多命名行開關(guān)都具有縮寫的形式,命令行開關(guān)的全名和縮寫之間具有一個映射關(guān)系(Switch Mapping)。以上述的這兩個命令行開關(guān)為例,我們可以采用首字母“a”和“r”來代表作為全名的“architecture”和“runtime”。如果采用縮寫的命令行開關(guān)名稱,那么我們就可以按照如下兩種方式指定CPU架構(gòu)和運(yùn)行時類型。
1: exec app –-a x64 –-r coreclr 2: exec app -a x64 -r coreclr綜上所示,我們一共有五種指定命名行開關(guān)的方式,其中三種采用命令行開關(guān)的全名,余下的兩種則使用命令行開關(guān)的縮寫形式。下表總結(jié)了這五種命名開關(guān)的指定形式所采用的原始參數(shù)以及縮寫與全名的映射關(guān)系。這里隱藏著一個重要的細(xì)節(jié),字符 “-” 只能以縮寫的形式指定命令行開關(guān)的指,但是 “--” 則支持全稱和縮寫形式。
| 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 |
原始的命令行參數(shù)總是體現(xiàn)為一個字符串?dāng)?shù)組, CommandLineConfigurationSource以字符串?dāng)?shù)組作為配置源,并利用對應(yīng)的ConfigurationProvider將它轉(zhuǎn)換成配置字典。如下面的代碼片斷所示,CommandLineConfigurationSource具有Args和SwitchMappings,前者正式代表承載著原始命令行參數(shù)的字符串?dāng)?shù)組,后者則保存了命令行開關(guān)的縮寫與全稱之間的映射關(guān)系。在實(shí)現(xiàn)的Build方法中,它根據(jù)這兩個屬性創(chuàng)建出一個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的繼承者。它的目的很明確,就是對體現(xiàn)為字符串?dāng)?shù)組的原始命令行參數(shù)進(jìn)行解析,并將解析出來參數(shù)名稱和值添加到配置字典中 。這一切都是在重寫的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: }在采用基于命令行參數(shù)作為配置源的時候,我們可以創(chuàng)建一個CommandLineConfigurationSource并將其注冊到ConfigurationBuilder之上。我們也可以調(diào)用IConfigurationBuilder接口的如下兩個擴(kuò)展方法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解析命令行參數(shù)采用的策略具有一個深刻的認(rèn)識,我們來演示一個簡單的實(shí)例。我們創(chuàng)建一個控制臺應(yīng)用,并添加針對 “Microsoft.Extensions.Configuration.CommandLine”這個NuGet包的依賴。在Main方法中,我們編寫了如下一段簡單的實(shí)例程序。
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: }如上面的代碼片斷所示,我們在一個無限循環(huán)中接收用戶指定的命令行參數(shù),并據(jù)此創(chuàng)建一個CommandLineConfigurationSource對象并將其注冊到ConfigurationBuilder之上。我們在創(chuàng)建這個CommandLineConfigurationSource對象的時候,還指定一個表示命令行開關(guān)映射關(guān)系的字典。接下來我們利用這個ConfigurationBuilder生成一個Configuration對象,并將其所有子配置節(jié)的Key和Value打印出來。我們運(yùn)行該程序后分別采用上述五種方式提供了命令行參數(shù),根據(jù)如下所示的輸出結(jié)果,會發(fā)現(xiàn)解析命令行參數(shù)生成的配置是完全等效的。
作者:蔣金楠
微信公眾賬號:大內(nèi)老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術(shù)資料,可以掃描左邊二維碼(或者長按識別二維碼)關(guān)注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 原文鏈接
總結(jié)
以上是生活随笔為你收集整理的.NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 与朱元思书翻译和原文一句一翻译
- 下一篇: lzg_ad:XPE操作系统镜像尺寸优化