.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;在本章节中&…