ASP.NET Core - 選項系統之選項配置

来源:https://www.cnblogs.com/wewant/archive/2023/03/15/17111517.html
-Advertisement-
Play Games

1. 選項 前面講完了.NET Core 下的配置系統,我們可以通過 IConfiguration 服務從各種來源的配置中讀取到配置信息,但是每次要用的時候都通過 Iconfiguration 讀取配置文件會比較不方便,而且效率低。.NET Core 體系下提供了一個選項系統,該功能用於實現以強類型 ...


1. 選項

前面講完了.NET Core 下的配置系統,我們可以通過 IConfiguration 服務從各種來源的配置中讀取到配置信息,但是每次要用的時候都通過 Iconfiguration 讀取配置文件會比較不方便,而且效率低。.NET Core 體系下提供了一個選項系統,該功能用於實現以強類型的方式對程式配置信息進行訪問,並且可以將選項類註入到依賴註入容器中進行管理和使用。

在進行配置信息的強類型選項綁定的時候,需要一個相應的選項類,該類推薦按 {Object}Options 命名規則進行命名,有以下特點:

  • 必須非抽象類
  • 必須包含公共無參的構造函數
  • 類中需要與配置項進行綁定的屬性必須擁有 public 的 get、set 訪問器,並且屬性的命名必須與配置鍵一直,不區分大小寫
  • 要確保配置項能夠轉換到其綁定的屬性類型
  • 該類的欄位不會被綁定

2. 選項配置方式

2.1 手動綁定

IConfiguration 服務通過 ConfigurationBinder 類擴展了 Get 和 Bind 兩個方法,這兩個方法可以將配置信息綁定到選項類上。這種方式在之前的 ASP.NET Core - 配置系統之配置讀取 一章有講到過,這裡再做一下演示:

首先在配置文件中添加以下節點:

"Blog": {
	"Title": "ASP.NET Core Options",
	"Content": "This is a blog about Options System in ASP.NET Core Framework.",
	"CreateTime": "2022-12-06"
}

之後定義一個選項類:

public class BlogOptions
{
	public const string Blog = "Blog";

	public string Title { get; set; }

	public string Content { get; set; }

	public DateTime CreateTime { get; set; }
}

然後,在任何可以獲取到 IConfiguration 服務的地方都可以通過 IConfiguration 服務進行綁定:

using OptionsSample.Options;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

var blog1 = new BlogOptions();
app.Configuration.GetSection(BlogOptions.Blog).Bind(blog1);
var blog2 = app.Configuration.GetSection(BlogOptions.Blog).Get<BlogOptions>();

Console.WriteLine(JsonSerializer.Serialize(blog1));
Console.WriteLine(JsonSerializer.Serialize(blog2));

app.Run();

image

這種方式依舊有些不方便,雖然也有好處,能夠監測到配置的修改,在應用運行中同步改變(如果相應的配置處理程式支持變更重載的化),但每次都要指定相應的節點,每次都要實時構建新的選項對象,並且選項系統也能做到配置更改時更新選項。

2.2 依賴註入配置

2.2.1 配置文件節點轉換選項

除了手動綁定的方式外,我們還可以在應用啟動的時候讀取相應的配置節點,配置成Options並同時註冊到依賴註入容器中,由依賴註入容器管理其生命周期,並且多次使用。我們可以像註冊其他的依賴註入關係一樣,在入口文件中通過 IServiceCollection 的 Configure 擴展方法進行配置。

// 通過配置文件讀取某一配置節點
//builder.Services.Configure<BlogOptions>(builder.Configuration.GetSection(BlogOptions.Blog));

這裡通過獲取特定的配置節點,並將其配置為選項,之後就可以在任何能夠進行依賴註入的地方使用註入的 BlogOptions 選項類了。

var blogOption = app.Services.GetRequiredService<IOptions<BlogOptions>>().Value;
Console.WriteLine(JsonSerializer.Serialize(blogOption));

這裡使用到的 Configure 方法是 OptionsConfigurationServiceCollectionExtensions 類中的擴展方法,選項系統中有好幾個同名的 Configure 方法,但是這些是並不是同一個方法的重載,下麵會詳細講到。

2.2.2 硬編碼配置選項

除了從配置文件中讀取配置節點轉換為選項之外,我們也可以直接在代碼中硬編碼指定選項內容,並註入到容器之中。

//硬編碼的方式設置配置信息,也可以在這裡讀取資料庫信息
builder.Services.Configure<BlogOptions>(option =>
{
    option.Title = "test";
    option.Content = "test hard code option";
    option.CreateTime = DateTime.Now;
});

這種情況用得不多,在這種情況下我們可以進行額外的一些邏輯,例如從資料庫中獲取一些信息。這裡的 Configure 方法是 OptionsServiceCollectionExtensions 擴展類中的方法。值得註意的是,在同時使用上面兩個方法配置同一個選項類的情況下,硬編碼配置的方式優先。

2.2.3 使用DI服務配置選項

在某些場景下,選項的配置需要比較複雜的邏輯,會依賴容器中的其他服務,我們還可以使用以下方式:

builder.Services.AddOptions<BlogOptions>()
    // 這裡可以通過 Configure 方法指定需要的服務, IServiceProvider 只是一個示例
    .Configure<IServiceProvider>((option, service) => // 接收的的第一個參數選項類對象,後面依次是所註入的服務
    {
        // 通過註入的服務執行相應的邏輯
        option.Title = "test DI Configure";
        option.Content = "test DI Configure";
        option.CreateTime = DateTime.Now;
    });

這裡的 Configure 方法,與上面的不一樣,不再是 IServiceCollection 的擴展方法,而是 OptionsBuilder 類中的方法,AddOptions 擴展方法在 OptionsServiceCollectionExtensions 中,返回一個 OptionsBuilder 對象。該方法有多個重載,最多支持5個服務來配置選項:

image

當使用這種方式和上面的硬編碼的方式同時配置同一個選項類的情況下,哪部分代碼後執行,最後選項類的內容就以哪部分為準。

2.2.4 命名選項

在一些情況下,應用中是存在多份配置結構相同,但具體配置值不同的配置信息的,例如以下的情況:

"FirstBlog": {
	"Title": "ASP.NET Core Options",
	"Content": "This is a blog about Options System in ASP.NET Core Framework.",
	"CreateTime": "2022-12-06"
},
"SecondBlog": {
	"Title": "ASP.NET Core Configuration",
	"Content": "This is a blog about Configuration System in ASP.NET Core Framework.",
	"CreateTime": "2022-12-08"
}

這種情況下,兩個配置節點其實可以用同一個選項類接收,它們的結構是一樣的。而命名選項就是為了應對這種情況,選項系統中允許為當前配置的選項命名,而選項系統區分同一個類型的不同選項也是根據名字進行區分的。事實上,.NET Core 中所有 Options 都是命名選項,當沒有顯式指定名字時(如上面講到的預設配置方式),使用的名字預設是Options.DefaultName,即string.Empty。

builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.Configure<BlogOptions>("Second", builder.Configuration.GetSection("SecondBlog"));

這裡要說明的是,上面我們從依賴註入容器中解析 IOptions 介面,從而獲取我們需要的選項值,但是使用命名選項的情況下是無法通過 IOptions 介面解析相應的選項的,必須通過 IOptionsMonitor 或者 IOptionsSnapshot 介面來解析。這三個介面的區別下麵會重點講。

var blog = app.Services.GetRequiredService<IOptions<BlogOptions>>().Value;
Console.WriteLine(JsonSerializer.Serialize(blog));
var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));
var secondBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("Second");
Console.WriteLine(JsonSerializer.Serialize(secondBlog));

image

2.2.4 後期配置

後期配置是指當我們通過前面的方法對一個選項類進行配置之後,可能還會因為其他業務邏輯需要對其中的配置信息進行修改,這時候我們可以通過後期配置對選項系統中已配置的選項內容進行替換。後期配置在所有的OptionsServiceCollectionExtensions.Configure 執行完成之後執行,即配置系統不管代碼順序,會先完成所有選項的配置,再執行後期配置。

builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.PostConfigure<BlogOptions>("First", options =>
{
	options.Title = "Post Config";
});

var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));

image

除了 PostConfigure 方法外,還有 PostConfigureAll 方法,前者對單一選項類單一命名的選項對象進行後期配置,如果沒有指定名稱則是預設名稱,後者會對統一選項類下的不同命名選項統一進行配置。

builder.Services.Configure<BlogOptions>("First", builder.Configuration.GetSection("FirstBlog"));
builder.Services.Configure<BlogOptions>("Second", builder.Configuration.GetSection("SecondBlog"));
builder.Services.PostConfigureAll<BlogOptions>(options =>
{
	options.Title = "Post Config";
});

var firstBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("First");
Console.WriteLine(JsonSerializer.Serialize(firstBlog));
var secondBlog = app.Services.GetRequiredService<IOptionsMonitor<BlogOptions>>().Get("Second");
Console.WriteLine(JsonSerializer.Serialize(secondBlog));

image

通過 IOptionsMonitor 或者 IOptionsSnapshot 介面解析選項,在配置來源更改的情況下,相應的選項內容也會隨之變化(兩者的適用場景不同), 後期配置邏輯在選項的每一次更改之後都會執行。



參考文章:
ASP.NET Core 中的選項模式 | Microsoft Learn
選項模式 - .NET | Microsoft Learn
面向 .NET 庫創建者的選項模式指南 - .NET | Microsoft Learn
理解ASP.NET Core - 選項(Options)



ASP.NET Core 系列:

目錄:ASP.NET Core 系列總結
上一篇:ASP.NET Core - 配置系統之自定義配置提供程式


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 讀寫Excel基本代碼 直接複製不一定能用 實體類 @ExcelIgnore 在導出操作中不會被導出 @ExcelProperty 在導入過程中 可以根據導入模板自動匹配欄位, 在導出過程中可用於設置導出的標題名字 @Getter @Setter public class Material{ @Ex ...
  • TypeScript 備忘清單 IT寶庫TypeScript開發速查清單包含最重要基礎、泛型、方法、class 等 TypeScript 強類型編程語言語法的快速參考備忘單。初學者的完整快速參考入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 Interface 介紹 內置類型基元 常見的 ...
  • Qt 學習筆記全系列傳送門: Qt 學習筆記 - 第一章 - 快速開始、信號與槽 Qt 學習筆記 - 第二章 - 添加圖片、佈局、界面切換 Qt 學習筆記 - 第三章 - Qt的三駕馬車之一 - 串口編程 + 程式打包成Windows軟體 【本章】Qt 學習筆記 - 第四章 - Qt的三駕馬車之二 ...
  • 背景 日常開發中,經常需要對一些響應不是很快的關鍵業務介面增加防重功能,即短時間內收到的多個相同的請求,只處理一個,其餘不處理,避免產生臟數據。這和冪等性(idempotency)稍微有點區別,冪等性要求的是對重覆請求有相同的效果和結果,通常需要在介面內部執行業務操作前檢查狀態;而防重可以認為是一個 ...
  • 預覽 技術實現 看過我上篇在 WPF 中實現 OpenGL 與 D3D 渲染的同學應該知道,我是依靠 WGL 中 WGL_NV_DX_interop 擴展與 D3D Surface 關聯併在使用該 Surface 實現渲染。 所以我們這次實現也是如此,但與 WPF 不同的是 WinUI 支持 D3D ...
  • 需求 swagger頁面按標簽Tags分組顯示。 沒有打標簽Tags的介面,預設歸到"未分組"。 分組內按介面路徑排序 說明 為什麼沒有使用GroupName對介面進行分組? 暫時不需要,以及不想點擊swagger頁面右上角那個下拉框。 當然Tags和GroupName不衝突,不影響通過GroupN ...
  • 背景: 我們項目一開始的所有提示都是中文,後來要做國際化。發現項目中的帶雙引號的中文居然有 2.3 w 多條!!!簡直讓人欲哭無淚... 如果使用人工改的話,首先不說正確率了。光是效率都是難難難。所以發揮了自己的才能寫了一個自動化工具。 思路: 首選讀取項目文件夾下的所有文件路徑 篩選路徑文件尾碼. ...
  • 接下來我們對依賴屬性進行一個簡單的剖析,從以下幾個方面入手吧。 1 - 為什麼是public static 首先說下為什麼是public 答:WPF有一種特殊屬性,叫附加屬性,需要直接訪問xxxxProperty的方法才能實現,所以xxxxProperty是public 的。 其次為什麼是靜態sta ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...