在ASP.NET Core中的App configuration 是通過configuration providers基於key-value對建立的。Configuration providers讀取配置檔案到key-value,從多種配置源中:
- Azure key Vault
- Command-line arguments
- Custom providers(installed or created)
- Directory files
- Environment variables
- In-memory .NET objects
- Setting files
用於提供Configuration配置的包是包含在Microsoft.AspNetCore.App metapackage裡。下面的程式碼示例,將會使用Microsoft.Extensions.Configuration名稱空間。
using Microsoft.Extensions.Configuration;
一.概述
1.Host vs App configuration(對比)
在應用配置和啟動之前,host被配置和launched(發動,開展)。Host 負責應用的startup和生命週期管理。應用和主機都是用這個主題描述的configuration providers來配置。主機配置的key-values對成為應用全域性配置的一部分。
2.Default configuration
ASP.NET Core基礎上的Web應用 dotnet new templates(模板)會呼叫CreateDefaultBuilder,當建立host時,CreateDefaultBuilder為應用提供預設的配置,以下面的順序:
(1).主機配置是被下面這些提供的:
- 以ASPNETCORE_為字首的環境變數(例如,ASPNETCORE_ENVIRONMENT)使用Environment Variables Configuration Provider. 當configuration key-values對被載入時,字首(ASPNETCORE_)被去掉。
- 命令列引數使用Command-line Configuration Provider提供
(2).應用配置是被下面這些提供的:
- appsettings.json使用File Configuration Provider提供
- appsetting.{Environment}.json使用File Configuration Provider提供
- 當應用執行在Development environment(開發環境), Secret Manager使用entry程式集(entry:入口)
- 環境變數使用Environment Variables Configuration Provider. 如果一個自定義字首被使用了,這個字首會在configuration key-value pairs被載入時去掉。(例如, PREFIX_ with .AddEnvironmentVariables(prefix:”PREFIX_”)).
- Command-line 引數使用command-line Configuration Provider
3.Security
採用下面的最佳實踐:
- 不要儲存密碼或者其他敏感資料在configuration provider code或者 plain text configuration files.
- 不要在開發和測試環境使用production secrets
- 在專案外,指定secrets,以便它們不會被意外提交到原始碼倉庫
4.Hierarchical configuration data(分層的配置資料)
在下面的JSON檔案中,結構化分層的兩個sections中存在四個key:
{ "section0": { "key0": "value", "key1": "value" }, "section1": { "key0": "value", "key1": "value" } }
當檔案被讀取到配置中時,唯一的key被建立,來維護原始配置源中的分層資料結構。
Section和key被使用冒號展開來維持原始結構:
section0:key0
section0:key1
section1:key0
section1:key1
如上,每個值都可以被唯一的取到。
GetSection和GetChildren方法可以被用來分離配置資料中的sections和section的children 。這些方法會在隨後的GetSection,GetChildren,和Exists描述。GetSection是在Microsoft.Extensions.Configuration包中,這個包是在Microsoft.AspNetCore.App metapackage.
5.Conventions(習慣,約定)
這裡是一些習慣的約定。
在應用啟動時,配置源按照它們的configuration provider被指定的順序來被讀取。
Configuration providers實現了變化檢測,Configuration providers可以重新載入配置,當一個基礎的設定被改變時。例如,File Configuration Provider和Azure key Value Configuration Provider實現了變化檢測。
IConfiguration在應用的依賴注入(DI)容器中是可用的。IConfiguration可以被注入到一個Razor Pages Pagemodel來包含一個配置的類。
public class IndexModel : PageModel { private readonly IConfiguration _config; public IndexModel(IConfiguration config) { _config = config; } // The _config local variable is used to obtain configuration // throughout the class. }
Configuration providers不能使用DI,因為當它們(Configuration providers)被host建立時,DI是不可用的。
Configuration keys採用下面的約定:
- Keys忽略大小寫。例如ConnectionString和connectionstring被認為是相同的鍵(keys)
- If a value for the same key is set by the same or different configuration providers, the last value set on the key is the value used.(如果同一個key的值被不同的資料提供器設定,會預設使用後面設定的,按configuration providers指定的順序)
- 分層keys
- 在Configuration API中,冒號:可以在所有平臺起作用
- 在環境變數中,冒號可能不能在所有平臺起作用。雙下劃線(__)被所有平臺支援並且轉化為冒號
- 在Azure Key Vault,分層的keys用--(兩個中槓)作為分割符,你必須提供程式碼來用冒號替換這兩個中槓。當secrets被載入到應用的配置中時。
- ConfigurationBinder支援繫結陣列到物件,在配置keys中使用array 標識體。陣列繫結會在Bind an array to class節描述。
Configuratian values採用下面的約定:
- Values都是string
- Null值不能被儲存在配置中或者繫結到物件
6.Providers
下面列出了ASP.NET Core應用可用的configuration providers:
Configuration sources(配置源)按照它們的configuration providers在startup中指定的順序被順序讀取。
典型的configuration providers的順序:
- Files (appsettings.json, appsettings.{Environment}.json, {Environment}是應用當前的執行環境)
- Azure Key Vault
- User secrets (Secret Manager)(僅用在開發環境)
- Environment variables
- Command-line arguments
這是一種通用的實踐,把命令列配置源放到最後,使它可以重寫其他配置源設定的配置。
注意:後面的配置會覆蓋前面的配置
7.ConfigureAppConfiguration
呼叫ConfigureAppConfiguration,當需要建立host來指定除了被CreateDefaultBuilder自動新增配置源外的其他配置源:
public class Program { public static Dictionary<string, string> arrayDict = new Dictionary<string, string> { {"array:entries:0", "value0"}, {"array:entries:1", "value1"}, {"array:entries:2", "value2"}, {"array:entries:4", "value4"}, {"array:entries:5", "value5"} }; public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddInMemoryCollection(arrayDict); config.AddJsonFile( "json_array.json", optional: false, reloadOnChange: false); config.AddJsonFile( "starship.json", optional: false, reloadOnChange: false); config.AddXmlFile( "tvshow.xml", optional: false, reloadOnChange: false); config.AddEFConfiguration( options => options.UseInMemoryDatabase("InMemoryDb")); config.AddCommandLine(args); }) .UseStartup<Startup>(); }
二.configuration provider講解
8.Command-line Configuration Provider
在執行時,CommandLineConfigurationProviders從command line argument key-value pairs中載入配置.
要想啟動command-line 配置,AddCommandLine擴充套件方法需要在ConfigurationBuilder例項中被呼叫。
AddCommandLine早已被CreateDefaultBuilder呼叫。如果你需要提供 app configuration 並且仍然可以用command-line arguments重寫配置,在ConfigureAppConfiguration中,呼叫app的額外的providers,並且在最後呼叫AddCommandLine.
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Call other providers here and call AddCommandLine last. config.AddCommandLine(args); }) .UseStartup<Startup>(); }
當直接建立一個WebHostBuilder時,呼叫UseConfiguration(另一種用法):
var config = new ConfigurationBuilder() // Call additional providers here as needed. // Call AddCommandLine last to allow arguments to override other configuration. .AddCommandLine(args) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
Example
示例應用功能利用靜態方法CreateDefaultBuilder來建立host,它包含對AddCommandLine的呼叫.
- 在專案目錄開啟命令列
- 把命令列引數用到 dotnet run 命令,dotnet run CommandLineKey=CommandLineValue
- 在應用執行之後,開啟瀏覽器到應用的 http://localhost:5000
- 觀察輸出(dotnet run)。
Arguments
值必須是下面的形式.=後面的值可以為null(例如,CommandLineKey=)
在同一個命令視窗中,不要混合使用=號和空格
dotnet run CommandLineKey1=value1 --CommandLineKey2=value2 /CommandLineKey3=value3 dotnet run --CommandLineKey1 value1 /CommandLineKey2 value2 dotnet run CommandLineKey1= CommandLineKey2=value2
Switch mappings
Switch mapping dictionary key rules:
- Swithes必須以-或者- - 開頭
- Swith mappings不能包含重複的keys
當建立host指定應用配置時,呼叫ConfigureAppConfiguration:
public class Program { public static readonly Dictionary<string, string> _switchMappings = new Dictionary<string, string> { { "-CLKey1", "CommandLineKey1" },//把命令列中key為-CLKey1的鍵替換為key為CommandLineKey1 { "-CLKey2", "CommandLineKey2" } }; public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } // Do not pass the args to CreateDefaultBuilder public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder() .ConfigureAppConfiguration((hostingContext, config) => { config.AddCommandLine(args, _switchMappings); }) .UseStartup<Startup>(); }
當switch mappings dictionary被建立後,包含下面的資料:
如果啟動應用時,switch-mapped keys被使用,configuration會在dictionary提供的key裡接收到配置值。
dotnet run -CLKey1=value1 -CLKey2=value2
在執行之前的命令後,configuration包含了值,如下表:
如上,可以看出Switch mappings的作用是:
把命令列中輸入的key替換為switch mapping中的key值。
9.Environment Variables Configuration Provider
要啟用environment variables configuration,需要呼叫AddEnvironmentVariables擴充套件方法。
AddEnvironmentVariables用來載入以ASPNETCORE_開頭的環境變數。
當建立host指定應用配置時,呼叫ConfigureAppConfiguration :
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Call additional providers here as needed. // Call AddEnvironmentVariables last if you need to allow // environment variables to override values from other // providers. config.AddEnvironmentVariables(prefix: "PREFIX_"); }) .UseStartup<Startup>(); }
當直接建立WebHostBuilder,呼叫UseConfiguration:
var config = new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
Example
示例應用功能利用靜態方法CreateDefaultBuilder來建立host,它包含對AddEnvironmentVariables的呼叫.
- 執行示例應用。瀏覽 http://localhost:5000
- 觀察環境變數的輸出。
環境變數以下面的開頭:
- ASPNETCORE_
- urls
- Logging
- ENVIRONMENT
- contentRoot
- AllowedHosts
- applicationName
- CommandLine
如果你希望在應用中暴露這些環境變數可用,在Pages/Index.cshtml.cs中改變FilteredConfiguration為下面:
FilteredConfiguration = _config.AsEnumerable();
Prefixes
當你應用一個字首到AddEnvironmentVariables方法,應用配置中的環境變數可以被過濾。例如,在字首CUSTOM_上過濾環境變數,應用字首到configuration provider:
var config = new ConfigurationBuilder() .AddEnvironmentVariables("CUSTOM_") .Build();
字首會被分離,當配置key-values pairs被建立時。
靜態方法CreateDefaultBuilder會建立一個WebHostBuilder來建立應用主機。當WebHostBuilder被建立時,可以在環境變數中找到ASPNETCORE_為字首的主機配置。
(1).Connection string prefixes
Configuration API(配置api)對於四個連線字串環境變數有特殊的處理規則。如果沒有字首作用到AddEnvironmentVariables,帶有下面字首的環境變數會被載入到應用中
當一個環境變數被發現,並且帶有上面四個之一的字首被載入到配置:
- configuration key通過移除環境變數字首來建立,並且增加一個configuration key section(ConnectionStrings)
- 一個新的configuration key-value pair被建立了,它代表database connection provider。(只有CUSTOMCONNSTR_,which has no stated provider).
10.File Configuration Provider
FileConfigurationProvider是從檔案系統中載入配置的基礎類。下面的configuration providers是作用特定的檔案型別:
- INI Configuration Provider
- JSON Configuration Provider
- XML Configuration Provider
INI Configuration Provider
IniConfigurationProvider從INI檔案中載入配置。
要啟用INI 檔案配置,在ConfigurationBuilder例項中呼叫AddIniFile擴充套件方法。
重寫一些指定配置選項:
- 檔案是否是可選的
- 如果檔案改變,配置是否重新載入
- IFileProvider用於獲取檔案
呼叫ConfigureAppConfiguration,當建立host指定應用配置時:
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddIniFile( "config.ini", optional: true, reloadOnChange: true); }) .UseStartup<Startup>(); }
基礎路徑(base path)是通過SetBasePath設定。SetBasePath是在Microsoft.Extensions.Configuration.FileExtension包中,這個包是在Microsoft.AspNetCore.App metapackage.
當直接建立一個WebHostBuilder,呼叫UseConfiguration:
var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddIniFile("config.ini", optional: true, reloadOnChange: true) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
一個INI configuration file示例:
[section0] key0=value key1=value [section1] subsection:key=value [section2:subsection0] key=value [section2:subsection1] key=value
上面的配置檔案載入下面的key和value:
- section0:key0
- section0:key1
- section1:subsection:key
- section2:subsection0:key
- section2:subsection1:key
JSON Configuration Provider
JsonConfigurationProvider從JSON檔案中載入配置.
要啟用JSON檔案配置,在ConfigurationBuilder例項上,呼叫AddJsonFile擴充套件方法。
重寫一些指定配置選項:
- 檔案是否是可選的
- 如果檔案改變,配置是否重新載入
- IFileProvider用於獲取檔案
AddJsonFile會自動呼叫兩次,當你用CreateDefaultBuilder初始化一個WebHostBuilder時。這個方法被呼叫載入配置,從下面的:
- appsettings.json - 這個檔案被首先讀取。這個檔案的環境版本可以重寫appsettings.json檔案提供的值。
- appsettings.{Environment}.json - 這個檔案的環境版本(environment version)依據於IHostingEnvironment.EnvironmentName被載入
呼叫ConfiureAppConfiguration,當建立host來指定應用配置,通過其他檔案而不是通過appsettings.json和appsettings.{Environment}.json檔案。
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddJsonFile( "config.json", optional: true, reloadOnChange: true); }) .UseStartup<Startup>(); }
當直接建立WebHostBuilder,呼叫UseConfiguration:
var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("config.json", optional: true, reloadOnChange: true) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
Example
示例應用利用靜態方法CreateDefaultBuilder來建立host,它包含兩次對AddJsonFile的呼叫。配置從appsettings.json和appsettings.{Environment}.json檔案中載入
- 執行示例應用。瀏覽 http://localhost:5000
- 觀察配置中的輸出
XML Configuration Provider
XmlConfigurationProvider從XML檔案中載入配置。
要啟用XML檔案配置,在ConfigurationBuilder例項上呼叫AddXmlFile擴充套件方法。
配置檔案的根節點被忽略了,當配置key-value pairs被建立了。不要在檔案中指定一個Document Type Definition(DTD)或者名稱空間。
呼叫ConfigureAppConfiguration,當建立host來指定應用的配置時:
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddXmlFile( "config.xml", optional: true, reloadOnChange: true); }) .UseStartup<Startup>(); }
當直接建立WebHostBuilder,呼叫UseConfiguration:
var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddXmlFile("config.xml", optional: true, reloadOnChange: true) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
(1).XML配置檔案可以使用distinct元素名稱對於repeating sections:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <section0> <key0>value</key0> <key1>value</key1> </section0> <section1> <key0>value</key0> <key1>value</key1> </section1> </configuration>
前面的配置檔案在家下面的key和value:
- section0:key0
- section0:key1
- section1:key0
- section1:key1
(2).還可以如下,用name屬性區分元素的形式:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <section name="section0"> <key name="key0">value</key> <key name="key1">value</key> </section> <section name="section1"> <key name="key0">value</key> <key name="key1">value</key> </section> </configuration>
之前的配合檔案載入下面的key和value:
- section:section0:key:key0
- section:section0:key:key1
- section:section1:key:key0
- section:section1:key:key1
(3).屬性也可以用於值上:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <key attribute="value" /> <section> <key attribute="value" /> </section> </configuration>
之前的配置檔案載入下面的key和value:
- key:attribute
- section:key:attribute
11.Key-per-file Configuration Provider
KeyPerFileConfigurationProvider使用一個目錄檔案作為configuration key-value pairs.其中key是檔名。 value包含檔案內容。Key-per-file Configuration Provider用於Docker hosting 場景。
要啟用key-per-file configuration, 在ConfigurationBuilder例項上,呼叫AddKeyPerFile擴充套件方法。檔案的目錄路徑(directoryPath)必須是絕對路徑。
Overloads permit specifying:
- Action<KeyPerFileConfigurationSource>委託 ,來配置source
- 目錄是否是可選的,和目錄路徑
在檔名字中,雙下劃線(__)用作一個configuration key 分隔符。例如,檔名Logging__LogLevel__System產生configuration key : Logging:logLevel:System
呼叫ConfigureAppConfiguration,當建立host來指定應用的配置時:
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); var path = Path.Combine( Directory.GetCurrentDirectory(), "path/to/files"); config.AddKeyPerFile(directoryPath: path, optional: true); }) .UseStartup<Startup>(); }
當直接建立WebHostBuilder時,呼叫UseConfiguration:
var path = Path.Combine(Directory.GetCurrentDirectory(), "path/to/files"); var config = new ConfigurationBuilder() .AddKeyPerFile(directoryPath: path, optional: true) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
12.Memory Configuration Provider
MemoryConfigurationProvider使用記憶體集合作為配置key-value pairs.
要使用in-memory collection configuration,在ConfigurationBuilder例項上,呼叫AddInMemoryCollection擴充套件方法。
The configuration provider可以使用IEnumerable<KeyValuePair<String,String>>來初始化。
當建立host來指定應用配置時,呼叫ConfigureAppConfiguration:
public class Program { public static readonly Dictionary<string, string> _dict = new Dictionary<string, string> { {"MemoryCollectionKey1", "value1"}, {"MemoryCollectionKey2", "value2"} }; public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddInMemoryCollection(_dict); }) .UseStartup<Startup>(); }
當直接建立WebHostBuilder時,呼叫UseConfiguration:
var dict = new Dictionary<string, string> { {"MemoryCollectionKey1", "value1"}, {"MemoryCollectionKey2", "value2"} }; var config = new ConfigurationBuilder() .AddInMemoryCollection(dict) .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseStartup<Startup>();
三.其他
13.GetValue
ConfigurationBinder.GetValue<T> 從配置檔案中用特定的key提出value,並且轉化為特定型別。如果這個key沒找到,可以提供預設值。
下面的例子:
- 用key為NumberKey,從配置中提取string value.如果NumberKey沒有在配置中找到,預設的value值99會被使用
- 把這個value轉化為int型別
- 通過page儲存值
public class IndexModel : PageModel { public IndexModel(IConfiguration config) { _config = config; } public int NumberConfig { get; private set; } public void OnGet() { NumberConfig = _config.GetValue<int>("NumberKey", 99); } }
14.GetSection, GetChildren, and Exists
{ "section0": { "key0": "value", "key1": "value" }, "section1": { "key0": "value", "key1": "value" }, "section2": { "subsection0" : { "key0": "value", "key1": "value" }, "subsection1" : { "key0": "value", "key1": "value" } } }
當檔案被讀取到配置中,如下:
- section0:key0
- section0:key1
- section1:key0
- section1:key1
- section2:subsection0:key0
- section2:subsection0:key1
- section2:subsection1:key0
- section2:subsection1:key1
GetSection
IConfiguration.GetSection用特定的subsection key提取一個configuration subsection.
要返回一個包含在section1中的key-value pairs的IConfigurationSection,呼叫GetSection並且應用section name:
var configSection = _config.GetSection("section1");
其中configSection沒有value,只有一個key和path.
相似的,要包含key為section2:subsection0的value,呼叫GetSection並且應用section path:
var configSection = _config.GetSection("section2:subsection0");
GetSection不會返回null。 如果沒有匹配的section,一個空的IConfigurationSection會被返回。
GetChildren
呼叫在section2上的IConfiguration.GetChildren包含:
- subsection0
- subsection1
var configSection = _config.GetSection("section2");
var children = configSection.GetChildren();
Exists
用ConfigurationExtensions.Exists來確定configuration section是否存在:
var sectionExists = _config.GetSection("section2:subsection2").Exists();
給出的例子中的資料,sectionExists是false,因為section2:subsection2這個section不存在。
15.Bind to a class
配置可以被繫結到類中。
示例包含一個Startship的model(Models/Starship.cs):
public class Starship { public string Name { get; set; } public string Registry { get; set; } public string Class { get; set; } public decimal Length { get; set; } public bool Commissioned { get; set; } }
Starship.json檔案中starship的section節建立了配置,當示例應用使用JSON Configuration Provider載入配置時。
{ "starship": { "name": "USS Enterprise", "registry": "NCC-1701", "class": "Constitution", "length": 304.8, "commissioned": false }, "trademark": "Paramount Pictures Corp. http://www.paramount.com" }
下面的配置被建立了:
示例應用利用key為starship來呼叫GetSection。Starship key-value pairs被分離。在繫結例項值後:
var starship = new Starship(); _config.GetSection("starship").Bind(starship); Starship = starship;
16.Bind to an object graph
示例繫結一個TvShow的model,它包含Metadata和Actors類:
public class TvShow { public Metadata Metadata { get; set; } public Actors Actors { get; set; } public string Legal { get; set; } } public class Metadata { public string Series { get; set; } public string Title { get; set; } public DateTime AirDate { get; set; } public int Episodes { get; set; } } public class Actors { public string Names { get; set; } }
示例應用有個tvshow.xml檔案包含配置資料:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <tvshow> <metadata> <series>Dr. Who</series> <title>The Sun Makers</title> <airdate>11/26/1977</airdate> <episodes>4</episodes> </metadata> <actors> <names>Tom Baker, Louise Jameson, John Leeson</names> </actors> <legal>(c)1977 BBC https://www.bbc.co.uk/programmes/b006q2x0</legal> </tvshow> </configuration>
配置被Bind方法繫結到了TvShow物件上。如下:
var tvShow = new TvShow(); _config.GetSection("tvshow").Bind(tvShow); TvShow = tvShow;
ConfigurationBinder.Get<T>繫結和返回指定的型別。Get<T>比使用Bind更方便。下面的程式碼展示了怎麼再之前的程式碼使用Get<T>,如下:
TvShow = _config.GetSection("tvshow").Get<TvShow>();
17.Bind an array to a c lass
Bind還支援繫結陣列.
In-memory array processing
考慮下面配置中的keys和values:
示例應用中的keys和values被使用Memory Configuration Provider被載入:
public class Program { public static Dictionary<string, string> arrayDict = new Dictionary<string, string> { {"array:entries:0", "value0"}, {"array:entries:1", "value1"}, {"array:entries:2", "value2"}, {"array:entries:4", "value4"}, {"array:entries:5", "value5"} }; public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddInMemoryCollection(arrayDict); config.AddJsonFile( "json_array.json", optional: false, reloadOnChange: false); config.AddJsonFile( "starship.json", optional: false, reloadOnChange: false); config.AddXmlFile( "tvshow.xml", optional: false, reloadOnChange: false); config.AddEFConfiguration( options => options.UseInMemoryDatabase("InMemoryDb")); config.AddCommandLine(args); }) .UseStartup<Startup>(); }
陣列跳過了索引為3的值。Configuration binder不能繫結null 值或者建立null entries在繫結物件中。
在示例中,一個類用來繫結配置資料:
public class ArrayExample { public string[] Entries { get; set; } }
配置資料被繫結到物件上:
var arrayExample = new ArrayExample();
_config.GetSection("array").Bind(arrayExample);
也可以使用ConfigurationBinder.Get<T>:
ArrayExample = _config.GetSection("array").Get<ArrayExample>();
ArrayExample的例項,接收到配置中的陣列資料:
在繫結物件中,索引為3有的是array:4的值。
要處理這樣的情況,可以這樣,使用一個額外的JOSN Configuration Provider處理丟失的key-value pair, ArrayExample.Entries匹配完整的配置陣列:
missing_value.json:
{ "array:entries:3": "value3" }
在ConfigureAppConfiguration中:
config.AddJsonFile("missing_value.json", optional: false, reloadOnChange:false);
下面的值會被載入到配置中:
如果ArrayExample類的例項是在JSON Configuration Provider包含索引3後被繫結,ArrayExample.Entries陣列會包含這個值:
JSON array processing
如果JSON檔案包含一個陣列,配置keys被建立為陣列的索引。下面的配置檔案中,subsection是一個陣列:
{ "json_array": { "key": "valueA", "subsection": [ "valueB", "valueC", "valueD" ] } }
JSON Configuration Provider讀取配置資料到下面所列:
在示例應用中,下面的類用來繫結配置資料:
public class JsonArrayExample { public string Key { get; set; } public string[] Subsection { get; set; } }
在繫結後,JsonArrayExample.Key有值valueA,subsection的值儲存在Subsection屬性的陣列中。
四.對於自定義configuration provider
18.Custom configuration provider
示例應用說明怎麼建立一個基礎的configuration provider,它可以使用Entity Framework從資料庫讀取資料。
provider有下面特徵:
- EF in-memory database是用於證明目的。
- 在startup中,provider讀取資料庫表到配置中.
- Reload-on-change沒有實現,所以在應用啟動後更新資料庫對於應用的配置沒有作用。
定義個EFConfigurationValue實體來儲存資料庫中的配置資料。
Models/EFConfigurationValues.cs:
public class EFConfigurationValue { public string Id { get; set; } public string Value { get; set; } }
增加一個EFConfigurationContext來儲存和取得配置值。
EFConfigurationProvider/EFConfigurationContext.cs:
public class EFConfigurationContext : DbContext { public EFConfigurationContext(DbContextOptions options) : base(options) { } public DbSet<EFConfigurationValue> Values { get; set; } }
建立一個類實現IConfigurationSource.
EFConfigurationProvider/EFConfigurationSource.cs:
public class EFConfigurationSource : IConfigurationSource { private readonly Action<DbContextOptionsBuilder> _optionsAction; public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction) { _optionsAction = optionsAction; } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new EFConfigurationProvider(_optionsAction); } }
建立一個自定義的configuration provider通過繼承ConfigurationProvider.這個configuration provider初始化資料庫,當它是空的時。
EFConfigurationProvider/EFConfigurationProvider.cs:
public class EFConfigurationProvider : ConfigurationProvider { public EFConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction) { OptionsAction = optionsAction; } Action<DbContextOptionsBuilder> OptionsAction { get; } // Load config data from EF DB. public override void Load() { var builder = new DbContextOptionsBuilder<EFConfigurationContext>(); OptionsAction(builder); using (var dbContext = new EFConfigurationContext(builder.Options)) { dbContext.Database.EnsureCreated(); Data = !dbContext.Values.Any() ? CreateAndSaveDefaultValues(dbContext) : dbContext.Values.ToDictionary(c => c.Id, c => c.Value); } } private static IDictionary<string, string> CreateAndSaveDefaultValues( EFConfigurationContext dbContext) { // Quotes (c)2005 Universal Pictures: Serenity // https://www.uphe.com/movies/serenity var configValues = new Dictionary<string, string> { { "quote1", "I aim to misbehave." }, { "quote2", "I swallowed a bug." }, { "quote3", "You can't stop the signal, Mal." } }; dbContext.Values.AddRange(configValues .Select(kvp => new EFConfigurationValue { Id = kvp.Key, Value = kvp.Value }) .ToArray()); dbContext.SaveChanges(); return configValues; } }
一個AddEFConfiguration擴充套件方法允許增加配置源到一個ConfigurationBuilder.
Extensions/EntityFrameworkExtension.cs:
public static class EntityFrameworkExtensions { public static IConfigurationBuilder AddEFConfiguration( this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> optionsAction) { return builder.Add(new EFConfigurationSource(optionsAction)); } }
下面的程式碼展示了怎麼在Program.cs中使用自定義的EFConfigurationProvider:
public class Program { public static Dictionary<string, string> arrayDict = new Dictionary<string, string> { {"array:entries:0", "value0"}, {"array:entries:1", "value1"}, {"array:entries:2", "value2"}, {"array:entries:4", "value4"}, {"array:entries:5", "value5"} }; public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); config.AddInMemoryCollection(arrayDict); config.AddJsonFile( "json_array.json", optional: false, reloadOnChange: false); config.AddJsonFile( "starship.json", optional: false, reloadOnChange: false); config.AddXmlFile( "tvshow.xml", optional: false, reloadOnChange: false); config.AddEFConfiguration( options => options.UseInMemoryDatabase("InMemoryDb")); config.AddCommandLine(args); }) .UseStartup<Startup>(); }
19.Access configuration during startup
在Startup.ConfigureServices中注入IConfiguration到Startup建構函式中來取得配置資料。要再Startup.Configure中取得配置資料,要麼直接注入IConfiguration到方法中,要麼使用建構函式中的例項:
public class Startup { private readonly IConfiguration _config; public Startup(IConfiguration config) { _config = config; } public void ConfigureServices(IServiceCollection services) { var value = _config["key"]; } public void Configure(IApplicationBuilder app, IConfiguration config) { var value = config["key"]; } }
20.Access configuration in a Razor Pages page or MVC view
在Razor Pages page或者MVC view中取得configuration settings.增加一個using指令為Microsoft.Extensions.Configuration 名稱空間,並且把IConfiguration注入到page或者view中。
在Razor Pages page:
@page @model IndexModel @using Microsoft.Extensions.Configuration @inject IConfiguration Configuration <!DOCTYPE html> <html lang="en"> <head> <title>Index Page</title> </head> <body> <h1>Access configuration in a Razor Pages page</h1> <p>Configuration value for 'key': @Configuration["key"]</p> </body> </html>
在MVC view中:
@using Microsoft.Extensions.Configuration @inject IConfiguration Configuration <!DOCTYPE html> <html lang="en"> <head> <title>Index View</title> </head> <body> <h1>Access configuration in an MVC view</h1> <p>Configuration value for 'key': @Configuration["key"]</p> </body> </html>
參考網址:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2