先上一段代碼,了解一下 .NET Core 配置數據的結構.
新建一個 控制台項目,添加一個文件 json.json ,文件內容如下:
{ "country": "cn", "person": { "id": 1, "address": { "addName": "chengdu" } } }
控制台代碼:
private static void Main(string[] args) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.AddJsonFile(path: @"E:\xxx\my\core\VS2017\MyConfig\Demo2\json.json", optional: false, reloadOnChange: true); IConfigurationRoot config = builder.Build(); Console.WriteLine(config["country"]);//cn Console.WriteLine(config["person:id"]);//1 Console.WriteLine(config["person:address:addname"]);//chengdu Console.ReadKey();//修改 json.json 文件中 "id":2 Console.WriteLine(config["person:id"]);//2 Console.ReadKey(); }
AddJsonFile 方法有多個重載,上面只給出了其中一個,3個參數分別表示:
path:文件的物理路徑;
optional: xml 文檔是這樣寫的:Whether the file is optional. 該文件是否可選.true 表示可選,即如果該文件不存在,不會拋出異常,下面的所有顯示都為空;false 表示不可選,即如果該文件不存在,會拋出異常.
reloadOnChange:如果文件修改了,是否重新加載.
配置提供程序
ASP.NET Core 常用的共有6種配置提供程序 :
- 命令行配置提供程序
- 環境變量配置提供程序
- 文件配置提供程序
- Key-per-file配置提供程序
- 內存配置提供程序
- 自定義配置提供程序
一.命令行配置提供程序 : AddCommandLine
1.新建一個 WebAPI 項目,新建一個 TestController
[Route("api/[controller]")] [ApiController] public class TestController : ControllerBase { private readonly IConfiguration _config; public TestController(IConfiguration configuration) { _config = configuration; } public string Get() { //讀取配置數據中 Key 為 CommandLineKey 的值,如果沒有這個 Key,則返回默認值: defaultValue
//讀取配置文件的方法后面會單獨講. return _config.GetValue("CommandLineKey","defaultValue"); } }
2.修改 CreateWebHostBuilder 方法
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var config = new ConfigurationBuilder().AddCommandLine(args).Build(); var hostBuilder = WebHost.CreateDefaultBuilder(args); return hostBuilder.UseConfiguration(config).UseStartup<Startup>(); } }
3.測試:
1)不傳參數
2)傳入參數
傳參的格式有多種:
dotnet run CommandLineKey1=Value1
dotnet run --CommandLineKey2=Value2
dotnet run --CommandLineKey3 Value3
dotnet run /CommandLineKey4=Value4
dotnet run /CommandLineKey5 Value5
此外,傳入的以單橫杠"-"或者雙橫杠"--"作為前綴的 Key 支持替換:
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { Dictionary<string, string> switchMapping = new Dictionary<string, string> { {"-key", "CommandLineKey"}, {"--key", "CommandLineKey"}, }; IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args, switchMapping).Build(); return WebHost.CreateDefaultBuilder().UseConfiguration(config).UseStartup<Startup>(); } }
測試圖:
兩種情況都會顯示
實際上,在 2.X 版本,CreateDefaultBuilder(args) 方法內部已經調用了 AddCommandLine(args) 方法,我們不需要再調一次了.源碼(部分)如下:
二.環境變量配置提供程序 : AddEnvironmentVariables
在 2.X 版本,CreateDefaultBuilder(args) 方法內部已經調用了 AddEnvironmentVariables() 方法,我們不需要再調一次了.源碼(部分)如下:
其實,紅框框起來的方法都是配置提供程序.
那么,環境變量到底有哪些呢?哪里可以看呢?
新建如下控制器:
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { private readonly IConfiguration _config; public TestController(IConfiguration configuration) { _config = configuration; } public IEnumerable<KeyValuePair<string, string>> GetAll() { return _config.AsEnumerable(); } public string Get(string key) { return _config.GetValue(key, "defaultValue"); } }
太多了,只截了其中一小部分:
我們試試查詢紅框標注的環境變量:
三.文件配置提供程序 : AddJsonFile , AddIniFile , AddXmlFile
以 AddJsonFile 為例講解,其他兩個都差不多.
從上面的源碼截圖中我們已經看到,在使用 CreateDefaultBuilder 方法初始化新的 WebHostBuilder 時,會自動調用 AddJsonFile 兩次,依次從下面兩個文件加載配置:
appsettings.json : 首先讀取該文件.
appsettings.{Environment}.json : 再讀取此文件,該文件中的配置會替代 appsettings.json 文件中的值.
示例: (紅色部分是自己加的)
appsettings.json:
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "FirstSection": { "SecondSection": "hello world" } }
appsettings.{Environment}.json:
{ "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } }, "FirstSection": { "SecondSection": "fuck u" } }
請求結果:
下面我們來創建自己的配置文件:
在當前項目下新建一個 jsonconfig.json 文件:
{ "id": 1, "name": "wjire" }
CreateWebHostBuilder 方法修改如下:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args); return hostBuilder.ConfigureAppConfiguration((context, builder) => { builder.AddJsonFile(Path.Combine(context.HostingEnvironment.ContentRootPath, "jsonconfig.json"), true, true); }).UseStartup<Startup>(); }
圖就不上了...
附:
AddIniFile 配置文件:
[section0] key0=value key1=value [section1] subsection:key=value [section2:subsection0] key=value [section2:subsection1] key=value
AddXmlFile 配置文件
<?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-per-file 配置提供程序 AddKeyPerFile
這個提供程序有點特別,它是將文件名(含擴展名)作為 Key,文件內容作為 Value.
示例:
在當前項目下新建一個 filename.txt 文件,文件內容就一句話: hello world
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args); return hostBuilder.ConfigureAppConfiguration((context, builder) => { //param1:文件所在目錄的物理路徑 //param2:該文件是否可選 builder.AddKeyPerFile(context.HostingEnvironment.ContentRootPath, true); }).UseStartup<Startup>(); }
控制器如下:
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { private readonly IConfiguration _config; public TestController(IConfiguration configuration) { _config = configuration; } public string Get(string key) { return _config.GetValue(key, "defaultValue"); } }
五. 內存配置提供程序 AddInMemoryCollection
這個很簡單,直接上圖:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { Dictionary<string, string> memoryCollection = new Dictionary<string, string> { {"id","1" }, {"name","wjire" } }; IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args); return hostBuilder.ConfigureAppConfiguration((context, builder) => { builder.AddInMemoryCollection(memoryCollection); }).UseStartup<Startup>(); }
六.自定義配置提供程序
該示例演示了如果使用EF創建從數據庫(MySql)讀取配置的提供程序.
第一步:安裝 MySqlEF
第二步:創建數據庫表:
CREATE TABLE `myconfigs` ( `Id` varchar(20) NOT NULL DEFAULT '', `value` varchar(20) NOT NULL DEFAULT '', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第三步:創建實體類
public class MyConfig { public string Id { get; set; } public string Value { get; set; } }
第四步:創建數據庫上下文
public class MyConfigContext : DbContext { public MyConfigContext(DbContextOptions<MyConfigContext> options) : base(options) { } public DbSet<MyConfig> MyConfigs { get; set; } }
第五步:創建配置數據源
public class MyConfigSource : IConfigurationSource { private readonly Action<DbContextOptionsBuilder> _optionsAction; public MyConfigSource(Action<DbContextOptionsBuilder> optionsAction) { _optionsAction = optionsAction; } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new MyConfigProvider(_optionsAction); } }
第六步:創建配置數據源提供器
public class MyConfigProvider : ConfigurationProvider { private Action<DbContextOptionsBuilder> OptionsAction { get; } public MyConfigProvider(Action<DbContextOptionsBuilder> optionsAction) { OptionsAction = optionsAction; } //從數據庫讀取配置 public override void Load() { DbContextOptionsBuilder<MyConfigContext> builder = new DbContextOptionsBuilder<MyConfigContext>(); OptionsAction(builder); using (MyConfigContext dbContext = new MyConfigContext(builder.Options)) { //Data 是基類 ConfigurationProvider 的屬性,用來存儲配置數據源的. Data = !dbContext.MyConfigs.Any()//判斷表是否有數據 ? CreateAndSaveDefaultValues(dbContext)//如果沒有數據,則添加一些數據,存儲到配置數據源中. : dbContext.MyConfigs.ToDictionary(c => c.Id, c => c.Value);//如果有數據,讀取出來,存儲到配置數據源中. } } private static IDictionary<string, string> CreateAndSaveDefaultValues(MyConfigContext dbContext) { Dictionary<string, string> configValues = new Dictionary<string, string> { { "1", "refuge" }, { "2", "36" }, { "3", "chengdu" } }; dbContext.MyConfigs.AddRange(configValues.Select(kvp => new MyConfig { Id = kvp.Key, Value = kvp.Value }).ToArray()); dbContext.SaveChanges(); return configValues; } }
第七步:創建擴展方法,對外公開自定義的數據提供程序
public static class MyConfigExtension { public static IConfigurationBuilder AddCustomConfigurationProviderApp(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> optionsAction) { return builder.Add(new MyConfigSource(optionsAction)); } }
第八步:創建測試用的控制器
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { private readonly IConfiguration _config; public TestController(IConfiguration configuration) { _config = configuration; } //查詢所有 public IEnumerable<KeyValuePair<string, string>> GetAll() { return _config.AsEnumerable(); } //查詢某個key public string Get(string key) { return _config.GetValue(key, "defaultValue"); } }
第九步:調用自定義配置提供程序
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args); return hostBuilder.ConfigureAppConfiguration((context, builder) => { builder.AddCustomConfigurationProviderApp(options => options.UseMySql("Server=localhost;Database=test;User=root")); }).UseStartup<Startup>(); } }
測試結果:
1)查詢所有,可以看到,我們新增的配置數據已經加到系統中了;(看右邊的滾動條就知道總共有很多很多配置...)
2)查詢特定key
如果使用該方式提供配置數據,需要注意以下兩點:
1.提供程序在啟動時就將數據庫表讀入配置,不會基於每個key查詢數據庫;
2.應用啟動后,更新數據庫,配置不會更新.
上面講了如果提供配置數據,下面講獲取配置的幾種常用方法:
一.GetValue 上面已經演示過了,這里不再重復.只把常用的重載方法列出來
GetValue<T>("key") 如果 key 不存在,則會拋異常;
GetValue<T>("key",T default) 如果 key 不存在,則返回默認值
二. T Get<T>()
對於下面這個 json 文件:
{ "name": "refuge", "age": 36, "address": { "city": "chengdu" } }
定義一個實體:
public class Person { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } } public class Address { public string City { get; set; } }
控制器:
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { private readonly IConfiguration _config; public TestController(IConfiguration configuration) { _config = configuration; }
public string Get() { var person = _config.Get<Person>(); return JsonConvert.SerializeObject(person); } }
調用結果:
但是這種方式,個人持反對態度,因為存儲在系統中的配置數據有很多很多,上述方法相當於讀取所有配置數據來反序列化.
三.GetSection
講該方法之前,有必要再熟悉一下配置在系統中的存儲結構到底是怎么樣的.
對於上面那個 json 文件:name": "refuge",
"age": 36, "address": { "city": "chengdu" } }
將其讀入配置后,存儲的結構如下:
{"key":"name","value":"refuge"}
{"key":"age","value":"36"}
{"key":"address","value":null} //通過查詢所有配置,確實有這一行,這意味着想通過 GetValue<string>("address") 反序列化為對象是不可能的...
{"key":"address:city","value":"chengdu"}]
對於上面這些配置,"refuge" 和 "36" 是可以通過第一個方法 GetValue<string>("name") , GetValue<string>("age") 來獲取.
而 "chengdu" 只能用 GetValue<string>("address:city") 方法獲取了.
因此,要通過該方法把上述json文件的內容轉成 Person 對象,則需要對 json 文件進行修改,添加一個 "person" 節點:
{ "person": { "name": "refuge", "age": 36, "address": { "city": "chengdu" } } }
Action:
public string Get() { //方法一: { Person person = _config.GetSection("person").Get<Person>(); return JsonConvert.SerializeObject(person); } //方法二: { //Person person = new Person(); //_config.GetSection("person").Bind(person); //return JsonConvert.SerializeObject(person); } }
方法一明顯要帥氣得多!
問題又來了,如果我只想反序列化 "address" 節點的值,怎么辦呢?下面這個方法可以完成.
四.GetChildren
Action:
public string Get() { IConfigurationSection firstChildSection = _config.GetSection("person").GetChildren().First(); Address address = firstChildSection.Get<Address>(); return JsonConvert.SerializeObject(address); }
貌似完了.
2019年1月6日補充:
上面提到的全是從當前項目加載配置,ASP.NET Core 2.0 提供了從外部程序集加載配置的方法
需要實現該接口:
public interface IHostingStartup { void Configure(IWebHostBuilder builder); }
示例:
新建類庫 : ExternalAssembly
namespace ExternalAssembly { public class ExternalConfig : IHostingStartup { public void Configure(IWebHostBuilder builder) { Dictionary<string, string> memoryCollection = new Dictionary<string, string> { {"id","1" }, {"name","refuge"} }; builder.ConfigureAppConfiguration((context, configBuilder) => { configBuilder.AddInMemoryCollection(memoryCollection); }); } } }
這里要特別注意:
ConfigureAppConfiguration 方法需要安裝nuget包:

創建一個 WebAPI 項目,添加對上述類庫的引用,並在 Program 類中添加對該類庫的調用聲明(紅色標注):
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; [assembly: HostingStartup(typeof(ExternalAssembly.ExternalConfig))] namespace Demo5 { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { return WebHost.CreateDefaultBuilder(args).UseStartup<Startup>(); } } }
圖就不上了.