.net 溫故知新:【8】.NET 中的配置從xml轉向json
一、配置概述
在.net framework平台中我們常見的也是最熟悉的就是.config
文件作為配置,控制台桌面程序是App.config
,Web就是web.config
,裏面的配置格式為xml格式。
在xml裏面有系統生成的配置項,也有我們自己添加的一些配置,最常用的就是appSettings節點,用來配置數據庫連接和參數。
使用的話就引用包System.Configuration.ConfigurationManager
之後取裏面的配置信息:System.Configuration.ConfigurationManager.AppSettings["ConnectionString"]
隨着技術的發展這種配置方式顯得冗餘複雜,如果配置項太多層級關係參數表達凌亂,在.net core開始也將配置的格式默認成了json格式,包括現在很多的其它配置也是支持的,比如java中常用的yaml格式,為什麼能支持這麼多讀取源和格式,其實質在於配置提供程序
。
目前.NET 中的配置是使用一個或多個配置提供程序
執行的。 配置提供程序使用各種配置源從鍵值對讀取配置數據,這些配置程序稍後我們會看到,讀取的配置源可以是如下這些:
- 設置文件,appsettings.json
- 環境變量
- Azure Key Vault
- Azure 應用配置
- 命令行參數
- 已安裝或已創建的自定義提供程序
- 目錄文件
- 內存中的 .NET 對象
- 第三方提供程序
二、配置初識
IConfiguration 接口是所有配置源的單個表示形式,給定一個或多個配置源,IConfiguration 類型提供配置數據的統一視圖。
上圖我們可能沒有直觀的感受,現在寫一個例子來看看
(1). 新建控制台應用程序:
創建控制台使用的是.net 6.0 框架,vs 2022。
安裝 Microsoft.Extensions.Configuration.Json NuGet 包,該包提供json配置文件讀取。
Install-Package Microsoft.Extensions.Configuration.Json
(2). 添加appsettings.json 文件
{
"person": {
"name": "XSpringSun",
"age": 18
}
}
(3). 使用json提供程序讀取json配置
new一個ConfigurationBuilder,添加json配置,AddJsonFile
是在包中的IConfigurationBuilder擴展方法,其它配置提供程序也是用這種擴展方法實現。
static void Main(string[] args)
{
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
Console.WriteLine(configuration["person:name"]);
Console.WriteLine(configuration["person:age"]);
Console.WriteLine("Hello, World!");
Console.ReadLine();
}
可以看到已經取到json配置文件中的值了,配置值可以包含分層數據。 分層對象使用配置鍵中的 : 分隔符表示。在下面的調試對象中我們可以看到實際configuration的Providers 提供程序數組有一個值,就是我們的JsonConfigurationProvider,並且JsonConfigurationProvider裏面已經讀取了json的數據存儲在Data數組中。
對於如上幾行代碼幹了什麼呢:
- 將 ConfigurationBuilder 實例化(new ConfigurationBuilder)。
- 添加 “appsettings.json” 文件,由 JSON 配置提供程序識別(AddJsonFile(“appsettings.json”))。
- 使用 configuration 實例獲取所需的配置
三、選項模式
這樣已經實現json進行配置讀取,但是取值的方式似乎和以前沒什麼太大變法,所以.net提供了選項模式,選項模式就是使用類來提供對相關設置組的強類型訪問。
我們創建一個Config
類用來轉換json:
namespace ConfigDemo
{
public class Config
{
public Person? person { get; set; }
}
public class Person {
public string? name { get; set; }
public int age { get; set; }
}
}
綁定配置
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
Config options = new Config();
ConfigurationBinder.Bind(configuration, options);
Person person = configuration.GetSection("person").Get<Person>();
Console.WriteLine(options.person.name);
Console.WriteLine(options.person.age);
Console.WriteLine("-----------GetSection獲取-------------");
Console.WriteLine(person.name);
Console.WriteLine(person.age);
用了兩種方式獲取配置,第一種使用ConfigurationBinder.Bind()
將整個配置綁定到對象Config
上,另外一種是使用IConfiguration的GetSection().Get<T>()
並返回指定的類型。兩種方式都可以使用,看實際需求和用途。
四、選項依賴注入
在控制台程序中我們引用DI注入包,然後演示下如何進行配置的注入。關於DI和IOC不清楚的看我上篇文章.net 溫故知新:【7】IOC控制反轉,DI依賴注入
- 新建一個測試類TestOptionDI
public class TestOptionDI
{
private readonly IOptionsSnapshot<Config> _options;
public TestOptionDI(IOptionsSnapshot<Config> options)
{
_options = options;
}
public void Test()
{
Console.WriteLine("DI測試輸出:");
Console.WriteLine($"姓名:{_options.Value.person.name}");
Console.WriteLine($"年齡:{_options.Value.person.age}");
}
}
在測試類中我們使用IOptionsSnapshot<T>
接口作為依賴注入,還有其它不同定義的接口用來配置注入,關於選項接口:。
不同接口可以配合讀取配置的不同方式起作用,IOptionsSnapshot接口可以在配置文件改變後不同作用域進行刷新配置。接着我們修改main方法,引入DI,並將AddJsonFile
方法的參數reloadOnChange設置為true,optional參數是否驗證文件存在,建議開發時都設置為true,這樣如果文件有問題會進行報錯。
注入配置這句services.AddOptions().Configure<Config>(e=>configuration.Bind(e))
是關鍵,通過容器調用AddOptions
方法註冊,然後Configure
方法裏面是一個委託方法,該委託的作用就是將配置的信息綁定到Config類型的參數e上。註冊到容器的泛型選項接口,這樣在TestOptionDI類構造函數注入就能注入IOptionsSnapshot
static void Main(string[] args)
{
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json",optional:true,reloadOnChange:true)
.Build();
//IServiceCollection 服務
ServiceCollection services = new ServiceCollection();
//注入配置
services.AddOptions().Configure<Config>(e=>configuration.Bind(e));
//注入TestOptionDI
services.AddScoped<TestOptionDI>();
using (var provider = services.BuildServiceProvider())
{
//獲取服務
var testOption = provider.GetRequiredService<TestOptionDI>();
testOption.Test();
}
Console.ReadLine();
}
為了測試IOptionsSnapshot接口在不同作用域會刷新配置,我們修改下main方法,用一個while循環在ReadLine時修改json文件值,不同的Scope里進行打印。
using (var provider = services.BuildServiceProvider())
{
while (true)
{
using (var scope = provider.CreateScope())
{
//獲取服務
var testOption = scope.ServiceProvider.GetRequiredService<TestOptionDI>();
testOption.Test();
}
Console.ReadLine();
}
}
這個功能在web中使用很方便,因為框架的一次請求就是一個作用域,所以我們修改了配置,下次請求就能生效了,而不用重啟服務。
五、其它配置
如最開始所說,不僅能配置json文件,由於各種提供程序,還可以配置其它的,但是根據配置的順序會進行覆蓋。我們只添加一個環境變量配置演示下:
首先添加提供程序包:Install-Package Microsoft.Extensions.Configuration.EnvironmentVariables
。
然後添加環境變量配置代碼AddEnvironmentVariables()
:
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json",optional:true,reloadOnChange:true)
.AddEnvironmentVariables()
.Build();
在VS中配置臨時環境變量
這裡有個扁平化配置,就是表示層級用冒號person:age
六、託管模式
對於web項目我們沒有進行這麼多操作它是怎麼配置的呢,其實框架已經自動幫我們做了,其它非web項目也可以使用這種託管模式,在Microsoft.Extensions.Hosting 包中,只需要使用簡單的代碼就能配置好。
IHost host = Host.CreateDefaultBuilder(args).Build();
await host.RunAsync();
其加載配置的優先級:
通過分析我們對整個配置如何運行的機制有了一個大體的了解,如果想詳細了解託管模式的還是建議看官方文檔:.NET配置