return
_info;
收工,搞定。就是這麼簡單。
補充:AddOptions & 對 IOptions 加入驗證
前面我們在註冊時,是使用
Configure
:
// 使用 Configure 註冊 Option
builder.Services.Configure<StrongholdInfoOptions>(
builder.Configuration.GetSection("StrongholdInfo"));
其實也可以使用
AddOptions
:
// 使用 Configure 註冊 Option
builder.Services.Configure<StrongholdInfoOptions>(
builder.Configuration.GetSection("StrongholdInfo"));
// 或是使用 AddOptions,這兩個做法最後都會呼叫 Configure<StrongholdInfoOptions>
// see: https://github.com/dotnet/extensions/issues/514
// ps: `BindConfiguration` 是比較新的語法,以前會使用 `Bind() + builder.Configuration.GetSection()`
builder.Services
.AddOptions<StrongholdInfoOptions>()
.BindConfiguration(StrongholdInfoOptions.SectionName);
這兩個語法最終會做同樣的事情,因為
Bind()
會去呼叫
Configure()
c# - 在 ASP.NET Core 中加載配置時,services.Configure() 和 services.AddOptions
().Bind() 之間有什麼區別?- Stack Overflow
Question: AddOptions
() vs. Multiple Configure
(…) · Issue #514 · dotnet/extensions (github.com)
只是
AddOptions
比較晚出現,並且後來又加入了更多自定義,用起來比較靈活。
例如我們可以用
ValidateDataAnnotations
來啟用屬性驗證:
(可參考 Microsoft Learn
選項模式
的「選項驗證」小節)
[Required]
[RegularExpression(@"^[\u4e00-\u9fa5]{1,10}$")] // 限定 1~10 個中文字
public string Name { get; set; }
builder.Services
.AddOptions<SettingsOptions>()
.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations() // 會在呼叫 .value 的時候進行驗證
// .Validate(config => {}) // 也可以自訂驗證邏輯
// .ValidateOnStart() // 也可以要求在啟動時就驗證
// 假如加了 ValidateDataAnnotations 的話,取值驗證失敗會噴 OptionsValidationException
SettingsOptions options = _config.Value;
catch (OptionsValidationException ex)
foreach (string failure in ex.Failures) // 畢竟可能一堆東西沒通過驗證嘛
_logger.LogError("Validation error: {FailureMessage}", failure);
如果需要自訂驗證器,可以搜尋
IValidateOptions
,可以自己實作
Validate()
,但我個人還沒遇到這麼複雜的狀況,這邊就不詳述。
Configure
直接又簡單,但
AddOptions
比較靈活。
我個人比較喜歡粗暴直接的做法,所以目前是都直接呼叫
Configure
而已。供各位參考
補充:IOptions、IOptionsMonitor、IOptionsSnapshot
提了 IOptions 就不能不提他的兩位哥哥:IOptionsMonitor、IOptionsSnapshot
[ApiController]
public class OptionsMonitorDemoController : ControllerBase
// 我們有三種 IOptions 相關的介面來取得設定檔內容
private readonly IOptions<StrongholdInfoOptions> _options;
private readonly IOptionsMonitor<StrongholdInfoOptions> _optionsMonitor;
private readonly IOptionsSnapshot<StrongholdInfoOptions> _optionsSnapshot;
這邊也迅速筆記一下。有興趣的朋友可以直接閱讀相關文章:
IOptions、IOptionsMonitor 以及 IOptionsSnapshot - wenhx - 博客园
ASP.NET Core 中的選項模式 | Microsoft Learn
的「選項介面」小節
IOptions
用起來最簡單方便,個人推👍
IOptions 會註冊為 Singleton,所以大家都會用同一組。也只有第一次建立的時候會抓設定檔的內容。後面就算跑去偷改檔案,也不會被影響(想更新?重啟站台吧)
如果設定檔不太常改動的話,直接用 IOptions 簡單做一做是最方便的,也省資源。
IOptionsMonitor
IOptionsMonitor 同樣也會註冊為 Singleton,但是它會去偷聽設定檔有沒有更新。
當設定檔有更新的時候 IOptionsMonitor 也會一起更新,所以能夠隨時取得目前版本的設定值
取值的方法名稱也很明確表達這點,大家都是
_options.Value
,
但 IOptionsMonitor 的是
_options.CurrentValue
_options = _options.Value,
_optionsMonitor = _optionsMonitor.CurrentValue,
如果我們的功能非常依賴設定值,而且又希望隨時更新(像留言區 Cash 大補充的 Hot Reload)的時候,就可以考慮使用 IOptionsMonitor。
但要小心如果 API 正在處理 Request,然後又剛好正在修改設定檔的話,可能會有一些靈異現象(?)
弄這篇筆記的時候也動手試了一下 IOptionsMonitor,但有點小佔版面,就放在最後的附錄(讓我之後可以回來抄)了。
IOptionsSnapshot
IOptionsSnapshot 會註冊為 Scope,所以每個請求進來的時候,都會各自去拿一次目前的設定檔內容,並且就用這一份設定檔內容處理這一次請求
我個人感覺最中規中矩。吃得到設定檔的變動,但也不會像
IOptionsMonitor
搞到前一秒還是 true 下一秒就是 false🤔
如果有改動 Config 的需求,又能接受下一組 Request 進來才吃到的時候(或是希望不要發生靈異現象),就可以考慮使用 IOptionsSnapshot。
大概這樣。但我個人平常都還是 IOptions 優先,
如果真的有需要即時反應設定檔的變動時,再把另外兩個拿出來討論吧。
前面介紹了 .Net Core 裡
appsettings.json
和
IOptions
的基本操作,也順便補充了一些簡單介紹。其他相關的操作,就放在延伸閱讀這邊,有興趣的朋朋們可以看看。
如果想根據不同環境(Dev, Prd 之類的)切換不同 appsettings.json:
ASP.NET Core 依環境載入不同 appsetting.json 設定 - 黑暗執行緒
如果你正在搞功能旗標(Feature Flag/Feature Toggle)然後看到這篇的話,
也可以嘗試看看
FeatureManagement
:
.Net: 使用 FeatureManagement 套件來實作 Feature Flag 功能切換吧
如果覺得到處都是 IOptions,想要降低對 IOptions 的依賴的話,可以綁到強型別裡:
[NETCore] ASP.NET Core 使用強型別取代 IOption
注入配置 ~ m@rcus 學習筆記
ASP.NET Core 的設定 | Microsoft Learn
ASP.NET Core 中的選項模式 | Microsoft Learn
[.NETCore] 如何取得 appsettings.json 組態設定 ~ m@rcus 學習筆記 (marcus116.blogspot.com)
IOptions、IOptionsMonitor以及IOptionsSnapshot - wenhx - 博客园 (cnblogs.com)
.NET Configuration with IOptions, IOptionsMonitor, and IOptionsSnapshot | by Ludmal De Silva | Medium
附錄:試一下 IOptionsMonitor
簡單比較一下修改 appsettings.json 後,IOptions 和 IOptionsMonitor 的資料差異,方便我以後需要複製貼上,或是哪天需要甩給朋友時使用。
public OptionsMonitorDemoController(
IOptions<StrongholdInfoOptions> options,
IOptionsMonitor<StrongholdInfoOptions> optionsMonitor)
_options = options;
_optionsMonitor = optionsMonitor;
[HttpGet("api/Demo/GetWithMonitor")]
public IEnumerable<object> GetWithMonitor()
var before = new
OptionsName = _options.Value.Name,
OptionsMonitorName = _optionsMonitor.CurrentValue.Name,
// 在這裡下中斷點,打開 appsettings.json 手動改資料
// 把 "Name": "劍閣" 改成 "Name": "羅馬"
System.Threading.Thread.Sleep(1000);
var after = new
OptionsName = _options.Value.Name,
OptionsMonitorName = _optionsMonitor.CurrentValue.Name,
return new object[] { before, after };
// {
// "optionsName": "劍閣",
// "optionsMonitorName": "劍閣"
// },
// {
// "optionsName": "劍閣",
// "optionsMonitorName": "羅馬"
// }
使用 IOptions 來註冊 & 注入
補充:AddOptions & 對 IOptions 加入驗證
補充:IOptions、IOptionsMonitor、IOptionsSnapshot
IOptions
IOptionsMonitor
IOptionsSnapshot
附錄:試一下 IOptionsMonitor