一個應用要執行起來,往往需要讀取很多的預設好的設定資訊,根據約定好的資訊或方式執行一定的行為。
設定的本質就是軟體執行的引數,在一個軟體實現中需要的引數非常多,如果我們以 Hard Code(寫死)的方式寫在應用程式碼中,這樣設定就會很亂,而且後續也不容易修改。亂而多,而且不容易修改,這就需要一個統一管理的地方,最常見的方式就是組態檔,這個也是開發人員非常熟悉的方式。
通過組態檔設定好軟體應用執行的各種引數之後,我們在開發過程中需要能夠讀取到組態檔的內容,根據設定內容進行軟體邏輯的判斷,實現完善的軟體行為邏輯。這一篇就是介紹 .NET Core 框架下怎麼使用設定系統,這也是 .NET Core 下的基礎設施之一。
設定讀取是設定系統最基本的操作,幾乎是每個開發人員都會進行的操作,一個開發人員可能不清楚設定系統是怎麼實現的,組態檔是怎麼解析的,但一定都做過讀取設定資訊的操作。.NET Core 框架下對於設定系統的使用最終暴露出來的介面是 ·IConfiguration·,它是供設定資料的統一檢視,設定讀取就通過這個介面的實現來進行。
預設建立的 ASP .NET Core 框架模板專案中預設有一個 appsettings.json 組態檔,這個也是 ASP .NET Core 中最常用的組態檔,在組態檔中新增多一個 Settings 節點,內容如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Settings": {
"key1": "value1",
"key2": 1,
"key3": true,
"key4": {
"subKey1": "value",
"subKey2": 1
},
"items": [ "item1", "item2", "item3" ]
}
}
我們要讀取組態檔中的內容,例如讀取「AllowedHosts」對於的值,只需要將其注入到需要的服務類中即可使用,ASP.NET Core 模板專案中使用Web主機構建和管理應用,在使用主機預設設定的時候已經將 Iconfiguration 服務註冊到依賴注入容器之中。
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly IConfiguration _configuration;
public WeatherForecastController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public Task Get()
{
var allowedHosts = _configuration["AllowedHosts"];
Console.WriteLine(allowedHosts);
// 設定鍵不區分大小寫
var allowedHosts = _configuration["AllowedHosts"];
Console.WriteLine(allowedHosts);
return Task.CompletedTask;
}
}
上面這種讀取方式是索引器方式,最簡單也是基本的方式,設定被載入到記憶體中是以鍵值對的方式存在的,我們可以通過設定鍵讀取設定值,鍵是字串,不區分大小寫,讀取出來的值都是字串。
組態檔中設定值往往不止一層,就像上面 appsettings.json 檔案中,Logging 節點下還有子節點,如果需要這種分層資料,可以使用 :
字元(英文冒號)分隔層次結構,例如獲取上面設定鍵 Default 對於的設定值。
// 以 : 作為分隔符,表示層級結構
var defalutLogLevel = _configuration["Logging:LogLevel:Default"];
如果設定值是陣列,需要讀取陣列中具體的某一個值,可以用該值在陣列中的索引作為key,例如讀取上面組態檔中的items陣列中的 item2。
// 讀取陣列,可以用值在陣列中的索引作為key
var item2 = _configuration["Settings:items:1"];
這種方式讀取設定有挺多不方便的地方,例如設定值是數值型的時候,需要我們直接轉換,例如一次只能讀取到一個設定值。微軟通過 Microsoft.Extensions.Configuration.Binder 中的ConfigurationBinder 類提供了一些 IConfiguration 的靜態方法,用於獲取設定值時進行自動轉換和繫結。
(1) GetValue
通過ConfigurationBinder中的GetValue擴充套件方法,一樣可以通過設定鍵從設定系統中讀取對於的設定值。該方法有多個過載,支援通過泛型的方式進行資料型別轉換,並且支援設定預設值。
var defaultLogLevel2 = _configuration.GetValue<string>("Logging:LogLevel:Default");
// 設定資訊中不包含 "Logging:LogLevel:Default" 這個Key時,以預設值 "Error" 返回
var defaultLogLevel3 = _configuration.GetValue<string>("Logging:LogLevel:Default", "Error");
(2) GetSection
這樣子有些情況下仍然無法滿足我們的需要,某一些情況下我們會需要直接讀取設定中的一部分節點,例如直接讀取上面設定中的 LogLevel 部分。IConfiguration 中的 GetSection 方法可以通過 Key 直接讀取某一個子節點。該方法的返回值是 IConfigurationSection 型別,永遠不會返回 null,IConfigurationSection 實際上是一個 IConfiguration 的派生介面,也就是說我們還可以從 IConfigurationSection 再去獲取我們需要的具體的設定值。
var section = _configuration.GetSection("Settings:key4");
var defaultLogLevel4 = section["Default"];
(3) Get
上面講到通過 GetSection 獲取到了組態檔中的一部分子節點,但是那樣仍然不方便,還是需要一個一個去讀取具體的值。可以通過 ConfigurationBinder.Get 擴充套件方法,將設定以強型別的方式繫結到物件上。
首先需要定義一個類來接收組態檔中的節點資訊
public class KeyOptions
{
public string subKey1 { get; set
public int subKey2 { get; set; }
}
然後通過以下方式進行繫結:
var keyOption1 = _configuration.GetSection("Settings:key4").Get<KeyOptions>();
(4) Bind
ConfigurationBinder.Bind 擴充套件方法與 Get 方法類似,也是用於將設定繫結為強型別物件,不過Bind的方法是繫結到一個已範例化的物件上,需要提供一個已存在的物件。
var keyOption2 = new KeyOptions();
_configuration.GetSection("Settings:key4").Bind(keyOption2);
(5) Exists
上面說過,GetSection 方法獲取設定中的子節點,返回值永遠不會為 null。如果我們傳入了一個不存在的key,肯定是獲取不到對於的值的,這種情況下還是需要判斷對於的子節點到底是不是真正存在的,這時候可以使用 Exists 方法。
var section = _configuration.GetSection("settings");
var exist = section.Exists();
除此之外,還有一個 GetChildren 方法,無需引數,用於獲取到當前設定節點的所有直接子節點的集合。
以上就是 .NET Core 體系下設定系統讀取設定的基本介紹,涉及到的型別最主要的是 IConfiguration 介面,除此之外還有上面提到的 IConfigurationSection 介面,以及 IConfigurationRoot 介面。
IConfigurationRoot 表示設定的根節點,是IConfiguration的派生介面,以下為介面的定義:
public interface IConfigurationRoot: IConfiguration{
// 存放了當前應用程式的所有設定提供程式
IEnumerable<IConfigurationProvider> Providers { get; }
// 強制從設定提供程式中過載設定
void Reload();
}
這裡可以看到一個關鍵的屬性 IEnumerable
IConfigurationSection 表示設定中的子節點,也是 IConfiguration 的派生介面,以下為介面的定義:
public interface IConfigurationSection: IConfiguration{
// 該子節點在其父節點中所表示的 key,即直接對應的key
string Key { get; }
// 該子節點在設定中的全路徑(從根節點開始,到當前節點以:符號分隔的路徑)
string Path { get; }
// 該子節點的 value。如果該子節點是葉子節點,則Value為該節點對於的值,若其下存在子節點,則其始終為 null
string Value { get; set; }
}
IConfigurationSection 介面通過以上三個屬性,結合 IConfiguration中的 GetChildren 方法來完整地表示的一個子節點,而 Exists 方法判斷節點是否為空,就是針對 IConfigurationSection 中的 Value 屬性和 GetChildren 方法來進行的。
public static class ConfigurationExtensions{
public static bool Exists(thisIConfigurationSection section){
if(section == null)
{
returnfalse;
}
returnsection.Value != null || section.GetChildren().Any();
}
}
參考文章:
ASP.NET Core 中的設定 | Microsoft Learn
設定 - .NET | Microsoft Learn
理解ASP.NET Core - 設定(Configuration)
ASP.NET Core 系列:
目錄:ASP.NET Core 系列總結
上一篇:ASP.NET Core - 依賴注入(四)