Configuration Bind
在上一篇文章中我們學到了該怎麼使用 Configuration 相關的 Package,程式將透過 DI 容器取得 ConfigurationRoot
可以方便
讀取 appsettings.json
中的設定值,不過目前是採用字典搭配 Key
值得方式來取回值有點不太方便而且有輸入錯誤的可能性,
因此我們可以先將設定值解析到一個專用的 Class 上在註冊到 DI 容器內,這樣之後就可以使用強型別方式來取值了,這也是昨天有提到
但沒有詳談的 Package Microsoft.Extensions.Configuration.Binder
背後會做的工作。
首先先建立一個新的 Web 專案
dotnet new web -o HelloWeb
根據昨天學到的知識我們已經知道專案有裝 Microsoft.Extensions.Hosting
並且註冊一個 IHost 其它事情 Dotnet 會幫我們註冊好
我們可以直接使用 appsettings.json
同時內部也有參考 Microsoft.Extensions.Configuration.Binder
所以我們可以直接使用。
接著修改 appsettings.json
的內容並建立 AppSettings.cs
// appsettings.json
{
"JsonSettings": {
"From": "MyJsonSettings"
}
}
// AppSettings.cs
public class AppSettings
{
public string From { get; set; }
}
根據目前現有的知識寫出以下程式,我們在昨天也有從 DI 容器內取出 IConfiguration 也就是 ConfigurationRoot
,今天建立了一個 AppSettings 類別
在透過 Bind 方法把 ConfigurationRoot
的設定值映射到 AppSettings 上,本質上你自己手動映射也可以達到同樣的效果。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var configuration = app.Services.GetRequiredService<IConfiguration>();
var settings = new AppSettings();
configuration.GetSection("JsonSettings").Bind(settings);
//var settings = new AppSettings();
//settings.From = configuration.GetSection("JsonSettings:From").Value;
Console.WriteLine(settings.From);
但是這種寫法好像沒有解決什麼問題,我們還是需要從 DI 取回 ConfigurationRoot
並透過字串取得設定值最後進行映射,等於是你之後每個 Controller 想要
取得設定值都要重複這個流程,那這樣強型別反而會變得有點麻煩乾脆以後都透過字串來取值就好了。
所以我們需要換一個思路,那就是讀取完設定值並映射完後就直接註冊到 DI 容器內,這樣之後就可以不用每次都取回 ConfigurationRoot
只需要取回 AppSettings
類別即可。
這一個版本的程式改在 WebApplication
Build 之前就先完成映射並註冊到 DI 容器內,這樣之後在任何一個 Controller 都能取得設定值
using HelloWeb;
var builder = WebApplication.CreateBuilder(args);
var settings = new AppSettings();
builder.Configuration.GetSection("JsonSettings").Bind(settings);
builder.Services.AddSingleton<AppSettings>(settings);
var app = builder.Build();
var mySetting = app.Services.GetRequiredService<AppSettings>();
Console.WriteLine(mySetting.From);
IOptions
其實在 Dotnet 有額外封裝了一系列的方法讓我們更加方便管理設定值, Options 相關的 Package 有以下幾個
- Microsoft.Extensions.Options
- Microsoft.Extensions.Options.ConfigurationExtensions
- Microsoft.Extensions.Options.DataAnnotations
這幾個 Package 最主要的功能就是管理設定值的生命週期,關鍵在於以下這幾個 Interface 和 AddOptions 方法
- IOptions
- IOptionsSnapshot
- IOptionsMonitor
public static IServiceCollection AddOptions(this IServiceCollection services)
{
ThrowHelper.ThrowIfNull(services);
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
return services;
}
從 AddOptions 方法可以看出 IOptions
和 IOptionsMonitor
註冊為 Singleton, IOptionsSnapshot
註冊為 Scoped,
這三個 Interface 主要使用來判斷設定值是否有改變,也就是說程式在運行時有沒有人手動修改 appsettings.json
如果不在意 appsettings.json
是否有被修改可以使用 IOptions
注入,但這也代表你需要重啟程式設定值才會生效。
IOptionsSnapshot
和 IOptionsMonitor
則代表需要監控 appsettings.json
的設定值一但有修改就要取代就有的設定值,
其中 IOptionsMonitor
代表即時監控一有修改就要馬上反應,IOptionsSnapshot
則是在目前 Scope 結束後的下一個 Scope 才會使用新設定。
接下來我們修改一下專案並修改注入的方式和建立新的 Controller。 這裡直接使用 Configure 來註冊類別,要注意這種註冊設定值的方式之後要取回就必須使用上面提到的三種 Options Interface,才能從 DI 容器取回設定值。
using HelloWeb;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.Configure<AppSettings>(
builder.Configuration.GetSection("JsonSettings"));
var app = builder.Build();
app.MapControllers();
app.Run();
// HelloController.cs
[ApiController]
[Route("[controller]")]
public class HelloController: ControllerBase
{
private readonly IOptions<AppSettings> _options;
public HelloController(IOptions<AppSettings> options)
{
_options = options;
}
[HttpGet(Name = "Get")]
public string Get()
{
return _options.Value.From;
}
}
完成後我們直接訪問 API https://localhost:7207/Hello
會回傳 MyJsonSettings 到網頁上,這時如果直接修改 appsettings.json
回傳值也不會時變動需要重啟程式設定值才會變動。
{
"JsonSettings": {
"From": "MyJsonSettings1"
}
}
接下來將 IOptions 改成 IOptionsSnapshot 並再次運行剛剛的流程,會發現即使不重啟程式設定值也會馬上進行更動。
[ApiController]
[Route("[controller]")]
public class HelloController: ControllerBase
{
private readonly IOptionsSnapshot<AppSettings> _options;
public HelloController(IOptionsSnapshot<AppSettings> options)
{
_options = options;
}
[HttpGet(Name = "Get")]
public string Get()
{
return _options.Value.From;
}
}
Summary
今天學習了如何將設定值改成強型別的形式讀取,並且了解使用 IOptions