.NET 8 中的 KeyedService

.NET 8 中的 KeyedService:新特性解析與使用示例

一、引言

在 .NET 8 的 Preview 7 版本中,引入了 KeyedService 支持。這一特性為開發者提供了按名稱(name)獲取服務的便利,在某些場景下,開發者無需再自行創建工廠類來管理服務。接下來,我們將深入探討 KeyedService 的使用方法、特殊情況以及存在的一些問題。

二、基本使用示例

1. 簡單示例代碼

var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedSingleton<IUserIdProvider, EnvironmentUserIdProvider>("env");
serviceCollection.AddKeyedSingleton<IUserIdProvider, NullUserIdProvider>("");using var services = serviceCollection.BuildServiceProvider();
var userIdProvider = services.GetRequiredKeyedService<IUserIdProvider>("");
Console.WriteLine(userIdProvider.GetUserId());var envUserIdProvider = services.GetRequiredKeyedService<IUserIdProvider>("env");
Console.WriteLine(envUserIdProvider.GetUserId());file interface IUserIdProvider
{string GetUserId();
}
file sealed class EnvUserIdProvider : IUserIdProvider
{public string GetUserId() => Environment.MachineName;
}
file sealed class NullUserIdProvider : IUserIdProvider
{public string GetUserId() => "(null)";
}

2. 代碼解釋

上述代碼展示了 KeyedService 的基本使用。我們通過 AddKeyedSingleton 方法注冊了兩個不同的 IUserIdProvider 實現,并分別使用不同的鍵(“env” 和 “”)進行標識。然后,通過 GetRequiredKeyedService 方法根據鍵來獲取相應的服務實例。

3. 輸出結果分析

運行代碼后,輸出結果為:

(null)
WEIHANLI - SURFACE

這表明我們成功地根據不同的鍵獲取到了對應的服務實例,并調用了其方法。

三、特殊的 serviceKey:AnyKey

1. 使用 AnyKey 捕獲未注冊的 serviceKey

var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedSingleton<IUserIdProvider, NullUserIdProvider>(KeyedService.AnyKey);using var services = serviceCollection.BuildServiceProvider();
var userIdProvider = services.GetRequiredKeyedService<IUserIdProvider>("");
Console.WriteLine(userIdProvider.GetUserId());var envUserIdProvider = services.GetRequiredKeyedService<IUserIdProvider>("env");
Console.WriteLine(envUserIdProvider.GetUserId());

2. 代碼解釋

這里我們使用 KeyedService.AnyKey 來注冊服務。當我們獲取服務時,即使使用了未注冊的鍵(如 “” 和 “env”),也不會報錯,而是使用 AnyKey 注冊的服務。

3. 輸出結果及對象驗證

輸出結果為:

(null)
(null)

為了驗證不同鍵獲取的服務實例是否為同一個對象,我們添加了以下代碼:

Console.WriteLine("userIdProvider == envUserIdProvider ?? {0}", userIdProvider == envUserIdProvider);

輸出結果為:

userIdProvider == envUserIdProvider ?? False

這表明不同的 serviceKey 獲取的是不同的對象。

4. serviceKey 為 null 的情況

serviceKeynull 時,情況比較特殊。在當前的 API 設計中,雖然允許 serviceKeynull,但實際上這會導致問題。例如:

var nullUserIdProvider = services.GetRequiredKeyedService<IUserIdProvider>(null);
Console.WriteLine(nullUserIdProvider.GetUserId());

會拋出異常:

System.InvalidOperationException: No service for type 'Net8Sample.<__Script>FE1DBF3BE6F8384813B223E3EAA03DBABDC4153F95C5B3EBB0E0807E84E7C20E4__IUserIdProvider' has been registered.

這說明當 serviceKeynull 時,并不會像使用 AnyKey 那樣獲取服務,而是直接報錯。并且,如果注冊 keyed service 時使用 null 作為 serviceKey,實際上相當于注冊了一個非 keyed service。

四、構造方法中的 ServiceKeyAttribute

1. 示例代碼

var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<MyNamedService>(KeyedService.AnyKey);
using var services = serviceCollection.BuildServiceProvider();
Console.WriteLine(services.GetRequiredKeyedService<MyNamedService>("Foo").Name);
Console.WriteLine(services.GetRequiredKeyedService<MyNamedService>("Hello").Name);file sealed class MyNamedService
{public MyNamedService([ServiceKey] string name){Name = name;}public string Name { get; }
}

2. 代碼解釋

在構造方法中,我們可以使用 ServiceKeyAttribute 來獲取注冊的 serviceKey。在上述示例中,我們使用 KeyedService.AnyKey 注冊服務,然后通過不同的鍵獲取服務實例,并輸出構造方法中獲取的 serviceKey

3. 輸出結果

Foo
Hello

這表明我們成功地在構造方法中獲取到了實際使用的 serviceKey

4. 類型一致性問題

需要注意的是,構造方法中的 serviceKey 類型和獲取服務時的類型應該保持一致,否則會拋出異常。例如:

Console.WriteLine(services.GetRequiredKeyedService<MyNamedService>(123).Name);

會導致異常:

System.InvalidOperationException: The type of the key used for lookup doesn't match the type in the constructor parameter with the ServiceKey attribute.

5. serviceKey 類型的靈活性

雖然需要類型一致,但 serviceKeyobject 類型,因此可以使用任意類型。例如:

var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<MyKeyedService>(KeyedService.AnyKey);
using var services = serviceCollection.BuildServiceProvider();Console.WriteLine(services.GetRequiredKeyedService<MyKeyedService>(new Category()
{Id = 1,Name = "test"
}).Name);

會輸出 test

五、Scoped Service 的問題

1. 示例代碼及異常

var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedScoped<IUserIdProvider, NullUserIdProvider>("");
using var services = serviceCollection.BuildServiceProvider();using var scope = services.CreateScope();
var newId = scope.ServiceProvider.GetRequiredKeyedService<IIdGenerator>("").NewId();
Console.WriteLine(newId);

運行上述代碼會拋出異常:

System.InvalidOperationException: This service provider doesn't support keyed services.

2. 問題分析

這表明目前對于 scoped service 的支持存在問題。在 aspnetcore 中,基于 HttpContext.RequestServices 獲取 keyedService 也會出現同樣的問題,因為 HttpContext.RequestServices 是一個 scoped service provider。不過,已經有 PR 修復了這個問題,預計在 RC1 版本中發布。

六、結合 Options 使用 KeyedService

1. 示例代碼

var serviceCollection = new ServiceCollection();serviceCollection.Configure<TotpOptions>(x =>
{x.Salt = "1234";
});
serviceCollection.AddKeyedTransient<ITotpService, TotpService>(KeyedService.AnyKey,(sp, key) =>new TotpService(sp.GetRequiredService<IOptionsMonitor<TotpOptions>>().Get(key is string name ? name : Options.DefaultName)));using var services = serviceCollection.BuildServiceProvider();
var totpService = services.GetRequiredKeyedService<ITotpService>(string.Empty);
Console.WriteLine("Totp1: {0}", totpService.GetCode("Test1234"));
var totpService2 = services.GetRequiredKeyedService<ITotpService>("test");
Console.WriteLine("Totp2: {0}", totpService2.GetCode("Test1234"));

2. 代碼解釋

通過結合 Options,我們可以方便地實現基于選項的命名服務。在上述示例中,我們根據不同的鍵獲取不同的 ITotpService 實例,并調用其 GetCode 方法。

3. 輸出結果

Totp1: 356934
Totp2: 626994

七、總結與見解

1. 優點

KeyedService 解決了一些命名服務的痛點,讓開發者可以更方便地按名稱獲取服務,減少了手動創建工廠類的工作量。結合 Options 使用時,還能實現更靈活的服務配置。

2. 不足

然而,目前該特性還存在一些問題,如 serviceKey 可以為 null 的設計不太合理,scoped service 支持存在 bug 等。不過考慮到這是預覽版,這些問題是可以接受的,希望在正式版中能夠得到妥善解決。

總體而言,KeyedService 是 .NET 8 中一個很有潛力的特性,為服務管理提供了新的思路和方法。開發者可以在項目中嘗試使用,但在正式項目中使用時需要謹慎考慮其穩定性。 ======================================================================
前些天發現了一個比較好玩的人工智能學習網站,通俗易懂,風趣幽默,可以了解了解AI基礎知識,人工智能教程,不是一堆數學公式和算法的那種,用各種舉例子來學習,讀起來比較輕松,有興趣可以看一下。
人工智能教程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915449.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915449.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915449.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Paimon對比基于消息隊列(如Kafka)的傳統實時數倉方案的優勢

弊端&#xff1a;數據重復 -> 優勢&#xff1a;Paimon 主鍵表原生去重原方案弊端 (Kafka)問題: 消息隊列&#xff08;Kafka&#xff09;是僅支持追加&#xff08;Append-Only&#xff09;的日志流。當 Flink 作業發生故障恢復&#xff08;Failover&#xff09;或業務邏輯迭代…

Linux Shell 命令 + 項目場景

shell 命令1. 基礎文件操作命令1.1 ls - 列出目錄內容1.2 find - 文件搜索2. 版本控制命令2.1 git - 版本控制系統2.2 高級 Git 操作3. 文本搜索命令3.1 grep - 文本搜索3.2 高級搜索技巧4. Android 構建系統命令4.1 source - 加載環境變量4.2 lunch - 選擇構建目標4.3 m - And…

A316-Mini-V1:超小尺寸USB高清音頻解碼器模組技術探析

引言 隨著便攜式音頻設備的普及&#xff0c;對小型化、高性能音頻解決方案的需求日益增長。本文將介紹一款極致小型化的高性能USB音頻解碼器模組——A316-Mini-V1&#xff0c;這是一款基于XMOS XU316芯片的微型音頻處理模組。產品概述 A316-Mini-V1是一款專為小尺寸產品設計的M…

低代碼平臺買saas好還是私有化好

選擇低代碼平臺采用SaaS還是私有化部署&#xff0c;應根據企業具體情況考慮安全性、成本控制、維護難度、擴展需求等因素。 其中&#xff0c;安全性是決定企業選擇的重要因素之一。私有化部署意味著企業能夠完全掌控數據和系統的安全管理&#xff0c;更適合對數據安全要求極高的…

基于SkyWalking的微服務APM監控實戰指南

基于SkyWalking的微服務APM監控實戰指南 1. 業務場景描述 隨著微服務在生產環境中大規模應用&#xff0c;系統鏈路復雜、實例彈性伸縮、灰度發布等特點都給性能監控和問題診斷帶來了新的挑戰。傳統的單機或輕量級監控方案已無法滿足微服務環境下的全鏈路、分布式追蹤和實時告警…

Python 進階(五): Excel 基本操作

目錄 1. 概述2. 寫入 2.1 使用 xlwt2.2 使用 XlsxWriter 3. 讀取4. 修改 1. 概述 在現實中&#xff0c;很多工作都需要與數據打交道&#xff0c;Excel 作為常用的數據處理工具&#xff0c;一直備受人們的青睞&#xff0c;而大部分人都是手動操作 Excel&#xff0c;如果數據量…

32、鴻蒙Harmony Next開發:使用動畫-動畫概述

???屬性動畫轉場動畫粒子動畫組件動畫動畫曲線動畫銜接動畫效果幀動畫&#xff08;ohos.animator&#xff09; UI&#xff08;用戶界面&#xff09;中包含開發者與設備進行交互時所看到的各種組件&#xff08;如時間、壁紙等&#xff09;。屬性作為接口&#xff0c;用于控制…

【STM32】485接口原理

485 通信實驗 這篇文章是對 RS485通信 的原理、硬件連接、接口芯片&#xff08;SP3485&#xff09;、總線結構等都有詳盡的說明。我們在此處進行清晰有條理的講解整理&#xff0c;便于學習和實驗操作。 在了解485接口通信原理之前&#xff0c;我們先復習一下串口&#xff1a;串…

亞馬遜二審攻防全攻略:預防、應對與長效合規之道

當店鋪收到二審通知&#xff0c;不少賣家會陷入焦慮與慌亂&#xff0c;只要掌握科學的預防策略與應對方法&#xff0c;不僅能降低二審風險&#xff0c;即便遭遇審核也能順利突圍。一、未雨綢繆&#xff1a;預防二審的四大核心策略夯實資料真實性根基資料的真實性與一致性是亞馬…

添加狀態信息

1首先在數據字典里加入可借閱和不可借閱狀態2導入數據字典export default {name: "Book",dicts: [book_borrow_status],//導入數據字典data() {return {formData: {name: null,author: null,num: null,price: null,typeId: null,status:null//新加狀態屬性},3設置狀態…

234、回文鏈表

題目&#xff1a;解答&#xff1a;對143稍作修改即可&#xff0c;判斷兩個指針指向的是否一直相等。終止條件為不等或者head2nullptrclass Solution { public:ListNode *rev(ListNode *head){ListNode *cur head;ListNode *pre nullptr;while(cur){ListNode * nxt cur->n…

第15次:商品搜索

實現用戶在頁面可自由搜索某個商品的功能。 第1步&#xff1a;準備搜索功能用到的庫 pip install whoosh pip install jieba pip install django-haystackwhoosh是搜索引擎&#xff0c;對英文支持較好&#xff0c;但對中文效果不佳。jieba為中文分詞庫&#xff0c;彌補whoosh…

《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》——0. 博客系列大綱

目錄【《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》系列簡介】第一部分&#xff1a;基礎入門與項目啟航第二部分&#xff1a;核心視覺算法開發第三部分&#xff1a;模擬完整工業流程第四部分&#xff1a;軟件打包與高級特性【《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》系列簡…

【Python】Python中的循環語句

循環語句導讀一、基本概念1.1 循環語句的執行流程1.2 循環語句的分類二、while語句三、for語句四、break與continue五、死循環六、循環中的else語句七、range()函數結語導讀 大家好&#xff0c;很高興又和大家見面啦&#xff01;&#xff01;&#xff01; 在上一篇內容中我們…

docker|Linux|以centos基礎鏡像為基礎制作nmap專用鏡像(鏡像瘦身計劃)

一、 最近由于某些場景下需要使用nmap&#xff0c;而nmap的rpm安裝包在源目標機器上使用有軟件沖突&#xff0c;因此&#xff0c;計劃使用docker部署nmap 具體計劃為 1、使用centos的基礎鏡像&#xff0c;在有網環境下&#xff0c;通過配置阿里云的yum倉庫&#xff0c;在cen…

基于單片機公交車報站系統/報站器

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽??????? 概述 公交車自動報站系統利用單片機作為核心控制器&#xff0c;結合GPS/北斗定位模塊、語音存…

Oracle 體系結構學習

1 認識Oracle后臺進程Oracle數據庫后臺進程是Oracle數據庫管理系統&#xff08;DBMS&#xff09;的核心組件&#xff0c;它們在后臺運行&#xff0c;負責數據庫的各種管理和維護任務。主要包括以下幾種&#xff1a;SMON (System Monitor)SMON負責數據庫的恢復操作&#xff0c;如…

構建一種安全的老式測試儀,用于具有限流燈泡,模擬儀表和可變輸出的交流設備

這個復古電路和電源測試儀的想法來自我需要一個簡單&#xff0c;安全&#xff0c;時尚的工具來測試和控制工作臺上的線路供電設備。商業解決方案要么太笨重&#xff0c;太昂貴&#xff0c;要么缺乏我喜歡的觸覺和模擬魅力。所以我決定自己造一個。這個測試儀的核心是一個老式的…

Redis5:Redis的Java客戶端——Jedis與SpringDataRedis詳解

目錄 1、Jedis客戶端 1.1使用過程 2、SpringDataRedis 2.1 SpingDataRedis介紹 2.2SpringDataRedis快速入門 2.3RedisTemplate的RedisSerializer 2.3.1RedisTemplate中JDK序列化局限性 2.3.2方式一&#xff1a;改變RedisTemplate的序列化方式 2.3.3RedisTemplate存儲一…

零基礎 “入坑” Java--- 十三、再談類和接口

文章目錄一、Object類1.獲取對象信息2.對象比較&#xff1a;equals方法二、再談接口1.比較相關接口2.Cloneable接口和深拷貝三、內部類1.匿名內部類2.實例內部類3.靜態內部類4.局部內部類在之前的學習中&#xff0c;我們已經了解了有關類以及接口的知識&#xff0c;在本章節中&…