news 2026/5/12 17:06:27

C#中的服务注册剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#中的服务注册剖析

一、服务注册的核心概念

1. 什么是服务注册?

服务注册是.NET 依赖注入(DI)体系中的核心操作,本质是将服务类型(接口 / 抽象类)与具体实现类型、生命周期绑定,并存储到 DI 容器中的过程。

  • 服务类型:对外暴露的抽象契约(通常是接口 / 抽象类),比如IUserService
  • 实现类型:服务类型的具体实现类,比如UserService
  • DI 容器:存储服务注册信息的 “容器”,程序运行时可从中 “取出”(解析)服务实例。
  • 生命周期:决定 DI 容器创建服务实例的规则,是服务注册的核心属性。
2. 服务的三种核心生命周期
生命周期类型核心特点
适用场景
瞬时(Transient)每次解析都创建全新实例无状态、轻量级服务(如工具类、数据验证器)
作用域(Scoped)同一个作用域内解析多次,返回同一个实例;不同作用域返回不同实例有状态的短期服务(如数据库上下文 DbContext)
单例(Singleton)
整个应用生命周期内,无论解析多少次,都返回同一个实例
无状态、全局复用的服务(如配置管理器、缓存服务)
3. 服务注册的核心价值
  • 解耦:调用方只依赖抽象(接口),不依赖具体实现,符合 “依赖倒置原则”;
  • 可替换:修改实现类时,只需修改注册逻辑,无需改动调用代码;
  • 自动管理生命周期:DI 容器自动创建、销毁实例,无需手动new和释放;
  • 简化测试:可轻松替换为模拟(Mock)服务,方便单元测试。

二、服务注册的核心用法

.NET 提供了IServiceCollection接口作为服务注册的入口,核心注册方法如下:

注册方法用途示例
AddTransient<TService, TImplementation>()注册瞬时服务services.AddTransient<IUserService, UserService>()
AddScoped<TService, TImplementation>()注册作用域服务services.AddScoped<IOrderService, OrderService>()
AddSingleton<TService, TImplementation>()注册单例服务services.AddSingleton<ICacheService, CacheService>()
AddSingleton<TService>(instance)注册已实例化的单例services.AddSingleton<ICacheService>(new CacheService())
Add<TService>()简写(默认瞬时)services.Add<UserService>()

三、控制台案例

步骤 1:创建控制台项目(环境准备)
  1. 打开 Visual Studio/VS Code,创建.NET 8(或更高版本)控制台项目;
  2. 确保项目文件(.csproj)包含Microsoft.Extensions.DependencyInjection包(默认已包含,若缺失可通过 NuGet 安装):
    <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <!-- 若缺失依赖,添加以下包引用 --> <ItemGroup> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> </ItemGroup> </Project>
    步骤 2:定义服务接口和实现类

先定义 3 个不同生命周期的服务:

using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; // ====================== 1. 定义服务接口(抽象契约) ====================== /// <summary> /// 瞬时服务接口:用户服务 /// </summary> public interface IUserService { // 获取服务实例ID(用于验证生命周期) Guid GetServiceInstanceId(); // 模拟业务方法:获取用户名 string GetUserName(int userId); } /// <summary> /// 作用域服务接口:订单服务 /// </summary> public interface IOrderService { Guid GetServiceInstanceId(); // 模拟业务方法:获取订单号 string GetOrderNo(int orderId); } /// <summary> /// 单例服务接口:缓存服务 /// </summary> public interface ICacheService { Guid GetServiceInstanceId(); // 模拟业务方法:设置缓存 void SetCache(string key, string value); // 模拟业务方法:获取缓存 string? GetCache(string key); } // ====================== 2. 实现服务类 ====================== /// <summary> /// 用户服务实现(瞬时) /// </summary> public class UserService : IUserService { // 每个实例唯一ID,用于验证生命周期 private readonly Guid _instanceId; // 构造函数:创建实例时生成唯一ID public UserService() { _instanceId = Guid.NewGuid(); Console.WriteLine($"【UserService】实例已创建,ID:{_instanceId}"); } public Guid GetServiceInstanceId() => _instanceId; public string GetUserName(int userId) { return userId switch { 1 => "张三", 2 => "李四", _ => "未知用户" }; } } /// <summary> /// 订单服务实现(作用域) /// </summary> public class OrderService : IOrderService { private readonly Guid _instanceId; public OrderService() { _instanceId = Guid.NewGuid(); Console.WriteLine($"【OrderService】实例已创建,ID:{_instanceId}"); } public Guid GetServiceInstanceId() => _instanceId; public string GetOrderNo(int orderId) { return $"ORDER_{orderId}_{DateTime.Now:yyyyMMdd}"; } } /// <summary> /// 缓存服务实现(单例) /// </summary> public class CacheService : ICacheService { private readonly Guid _instanceId; // 模拟缓存存储 private readonly Dictionary<string, string> _cacheDict = new(); public CacheService() { _instanceId = Guid.NewGuid(); Console.WriteLine($"【CacheService】实例已创建,ID:{_instanceId}"); } public Guid GetServiceInstanceId() => _instanceId; public void SetCache(string key, string value) { _cacheDict[key] = value; Console.WriteLine($"【CacheService】缓存已设置:{key} = {value}"); } public string? GetCache(string key) { _cacheDict.TryGetValue(key, out var value); return value; } }
步骤 3:服务注册与解析

编写主程序,完成服务注册、不同生命周期的解析验证:

/// <summary> /// 主程序 /// </summary> class Program { static void Main(string[] args) { Console.WriteLine("===== .NET服务注册与依赖注入演示 ====="); Console.WriteLine(); // ====================== 步骤1:创建服务集合(ServiceCollection),用于注册服务 ====================== // IServiceCollection是服务注册的容器,本质是存储服务描述的列表 var services = new ServiceCollection(); // ====================== 步骤2:注册服务(核心操作) ====================== Console.WriteLine("【第一步】开始注册服务..."); // 1. 注册瞬时服务:每次解析都创建新实例 services.AddTransient<IUserService, UserService>(); // 2. 注册作用域服务:同一个作用域内实例唯一 services.AddScoped<IOrderService, OrderService>(); // 3. 注册单例服务:整个应用生命周期内实例唯一 // 方式1:通过类型注册(推荐,容器自动创建实例) services.AddSingleton<ICacheService, CacheService>(); // 方式2:直接注册已实例化的单例(适用于需要手动初始化的场景) // var cacheInstance = new CacheService(); // services.AddSingleton<ICacheService>(cacheInstance); Console.WriteLine("【第一步】服务注册完成!"); Console.WriteLine(); // ====================== 步骤3:构建服务提供器(ServiceProvider),用于解析服务 ====================== // ServiceProvider是DI容器的核心,负责根据注册信息创建/解析服务实例 using var serviceProvider = services.BuildServiceProvider(); // ====================== 步骤4:验证不同生命周期的服务特性 ====================== Console.WriteLine("【第二步】验证瞬时服务(Transient)特性:每次解析都是新实例"); VerifyTransientService(serviceProvider); Console.WriteLine(); Console.WriteLine("【第三步】验证作用域服务(Scoped)特性:同作用域内实例唯一,不同作用域不同实例"); VerifyScopedService(serviceProvider); Console.WriteLine(); Console.WriteLine("【第四步】验证单例服务(Singleton)特性:全局唯一实例"); VerifySingletonService(serviceProvider); Console.WriteLine(); Console.WriteLine("【第五步】服务依赖注入演示(服务嵌套调用)"); VerifyServiceDependency(serviceProvider); Console.WriteLine(); Console.WriteLine("===== 演示结束 ====="); Console.ReadLine(); } #region 辅助方法:验证不同生命周期的服务 /// <summary> /// 验证瞬时服务:每次解析都是新实例 /// </summary> private static void VerifyTransientService(IServiceProvider serviceProvider) { // 第一次解析瞬时服务 var userService1 = serviceProvider.GetRequiredService<IUserService>(); Console.WriteLine($"第一次解析IUserService,实例ID:{userService1.GetServiceInstanceId()}"); Console.WriteLine($"业务方法调用:用户1的名称 = {userService1.GetUserName(1)}"); // 第二次解析瞬时服务(应该是新实例) var userService2 = serviceProvider.GetRequiredService<IUserService>(); Console.WriteLine($"第二次解析IUserService,实例ID:{userService2.GetServiceInstanceId()}"); Console.WriteLine($"业务方法调用:用户2的名称 = {userService2.GetUserName(2)}"); // 验证:两次解析的实例ID是否不同(瞬时服务特性) Console.WriteLine($"瞬时服务验证结果:两次实例是否相同? {userService1.GetServiceInstanceId() == userService2.GetServiceInstanceId()}"); } /// <summary> /// 验证作用域服务:同作用域内实例唯一,不同作用域不同实例 /// </summary> private static void VerifyScopedService(IServiceProvider serviceProvider) { // 作用域1:创建第一个作用域 using (var scope1 = serviceProvider.CreateScope()) { var scopeProvider1 = scope1.ServiceProvider; // 作用域1内第一次解析 var orderService1_1 = scopeProvider1.GetRequiredService<IOrderService>(); Console.WriteLine($"作用域1 - 第一次解析IOrderService,实例ID:{orderService1_1.GetServiceInstanceId()}"); Console.WriteLine($"业务方法调用:订单1的编号 = {orderService1_1.GetOrderNo(1)}"); // 作用域1内第二次解析(应该是同一个实例) var orderService1_2 = scopeProvider1.GetRequiredService<IOrderService>(); Console.WriteLine($"作用域1 - 第二次解析IOrderService,实例ID:{orderService1_2.GetServiceInstanceId()}"); Console.WriteLine($"作用域1内验证:两次实例是否相同? {orderService1_1.GetServiceInstanceId() == orderService1_2.GetServiceInstanceId()}"); } // 作用域2:创建第二个作用域 using (var scope2 = serviceProvider.CreateScope()) { var scopeProvider2 = scope2.ServiceProvider; // 作用域2内解析(应该是新实例) var orderService2 = scopeProvider2.GetRequiredService<IOrderService>(); Console.WriteLine($"作用域2 - 解析IOrderService,实例ID:{orderService2.GetServiceInstanceId()}"); Console.WriteLine($"业务方法调用:订单2的编号 = {orderService2.GetOrderNo(2)}"); } } /// <summary> /// 验证单例服务:全局唯一实例 /// </summary> private static void VerifySingletonService(IServiceProvider serviceProvider) { // 第一次解析单例服务 var cacheService1 = serviceProvider.GetRequiredService<ICacheService>(); cacheService1.SetCache("user_1", "张三"); Console.WriteLine($"第一次解析ICacheService,实例ID:{cacheService1.GetServiceInstanceId()}"); Console.WriteLine($"缓存读取:user_1 = {cacheService1.GetCache("user_1")}"); // 第二次解析单例服务(应该是同一个实例) var cacheService2 = serviceProvider.GetRequiredService<ICacheService>(); Console.WriteLine($"第二次解析ICacheService,实例ID:{cacheService2.GetServiceInstanceId()}"); Console.WriteLine($"缓存读取(复用第一次设置的值):user_1 = {cacheService2.GetCache("user_1")}"); // 在作用域内解析单例(仍然是同一个实例) using (var scope = serviceProvider.CreateScope()) { var cacheService3 = scope.ServiceProvider.GetRequiredService<ICacheService>(); Console.WriteLine($"作用域内解析ICacheService,实例ID:{cacheService3.GetServiceInstanceId()}"); Console.WriteLine($"缓存读取:user_1 = {cacheService3.GetCache("user_1")}"); } // 验证:所有解析的实例ID是否相同 var isSame = cacheService1.GetServiceInstanceId() == cacheService2.GetServiceInstanceId(); Console.WriteLine($"单例服务验证结果:所有实例是否相同? {isSame}"); } /// <summary> /// 验证服务依赖注入:一个服务依赖另一个服务 /// </summary> private static void VerifyServiceDependency(IServiceProvider serviceProvider) { // 先注册一个依赖其他服务的新服务 // 定义一个业务服务,依赖IUserService和ICacheService services.AddTransient<IBusinessService, BusinessService>(); // 注意:这里需要先把services变量改为类级别的静态变量,或重新注册 // 重新构建服务提供器(因为新增了注册) using var newServiceProvider = services.BuildServiceProvider(); var businessService = newServiceProvider.GetRequiredService<IBusinessService>(); businessService.DoBusiness(1); } #endregion } // ====================== 补充:服务依赖的示例 ====================== /// <summary> /// 业务服务接口(依赖其他服务) /// </summary> public interface IBusinessService { void DoBusiness(int userId); } /// <summary> /// 业务服务实现(构造函数注入IUserService和ICacheService) /// </summary> public class BusinessService : IBusinessService { // 依赖的服务通过构造函数注入(DI容器自动解析) private readonly IUserService _userService; private readonly ICacheService _cacheService; // 构造函数注入是.NET DI的默认方式(推荐) public BusinessService(IUserService userService, ICacheService cacheService) { _userService = userService; _cacheService = cacheService; Console.WriteLine($"【BusinessService】实例已创建,依赖的IUserService实例ID:{userService.GetServiceInstanceId()}"); Console.WriteLine($"【BusinessService】实例已创建,依赖的ICacheService实例ID:{cacheService.GetServiceInstanceId()}"); } public void DoBusiness(int userId) { var userName = _userService.GetUserName(userId); _cacheService.SetCache($"business_user_{userId}", userName); Console.WriteLine($"【BusinessService】业务处理完成:用户{userId}的名称是{userName},已缓存"); } }
步骤 4:代码关键说明
  1. 服务注册核心

    • ServiceCollection是 “注册清单”,通过AddXxx方法将服务类型与实现类型、生命周期绑定;
    • BuildServiceProvider()将注册清单转换为可解析服务的ServiceProvider(DI 容器)。
  2. 服务解析方法

    • GetRequiredService<T>():强制解析服务,若服务未注册则抛出异常(推荐);
    • GetService<T>():解析服务,若未注册则返回null
    • CreateScope():创建作用域,作用域内解析的 Scoped 服务实例唯一。
  3. 依赖注入方式

    • 示例中BusinessService通过构造函数注入依赖的IUserServiceICacheService,这是.NET DI 的默认且推荐的方式;
    • DI 容器会自动解析依赖链:解析IBusinessService时,先解析其构造函数中的IUserServiceICacheService,再创建BusinessService实例。
步骤 5:运行结果分析

运行程序后,会看到以下关键输出:

  • 瞬时服务:两次解析的IUserService实例 ID 完全不同;
  • 作用域服务:同一个作用域内两次解析的IOrderService实例 ID 相同,不同作用域 ID 不同;
  • 单例服务:无论直接解析还是在作用域内解析,ICacheService的实例 ID 始终相同,且缓存数据可复用;
  • 依赖注入BusinessService创建时,DI 容器自动注入依赖的服务实例,无需手动new

四、扩展知识点

  1. 服务注册的其他方式
    • 泛型服务注册:services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>))
    • 工厂模式注册:services.AddTransient<IUserService>(sp => new UserService("自定义参数"))
  2. 服务生命周期注意事项
    • 禁止在 “短生命周期服务” 中注入 “长生命周期服务”(如 Scoped 服务注入 Singleton 服务),会导致 Scoped 服务被 “提升” 为 Singleton,引发线程安全问题;
    • 瞬时服务可注入任意生命周期的服务,单例服务只能注入单例服务。
  3. 控制台项目 vs Web 项目
    • Web 项目中,每个 HTTP 请求对应一个作用域(Scoped),因此 Scoped 服务默认在单个请求内唯一;
    • 控制台项目需手动创建作用域(CreateScope()),否则解析 Scoped 服务会抛出异常。

总结

  1. 核心概念:服务注册是将 “抽象服务类型 - 具体实现类型 - 生命周期” 绑定并存储到 DI 容器的过程,目的是解耦和自动管理实例;
  2. 核心操作:通过ServiceCollectionAddTransient/AddScoped/AddSingleton完成注册,通过ServiceProvider解析服务;
  3. 生命周期关键:瞬时(每次新实例)、作用域(同作用域唯一)、单例(全局唯一),需根据业务场景选择。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 15:04:01

10 个AI论文工具,助你搞定继续教育毕业论文!

10 个AI论文工具&#xff0c;助你搞定继续教育毕业论文&#xff01; AI 工具如何助力论文写作&#xff0c;让学术之路更轻松 在继续教育的求学道路上&#xff0c;毕业论文往往是学生需要面对的最大挑战之一。无论是选题、资料收集、大纲搭建&#xff0c;还是初稿撰写与反复修…

作者头像 李华
网站建设 2026/5/9 15:15:27

9个AI写作工具,专科生论文格式难题轻松解决!

9个AI写作工具&#xff0c;专科生论文格式难题轻松解决&#xff01; AI工具的崛起&#xff0c;让论文写作不再难 随着人工智能技术的不断发展&#xff0c;越来越多的专科生在面对论文写作时&#xff0c;开始借助AI工具来提升效率、降低难度。尤其是在论文格式、语言表达以及内容…

作者头像 李华
网站建设 2026/5/11 10:31:36

AI助力Element UI分页组件开发:el-pagination智能优化

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 使用AI生成一个基于Element UI的el-pagination分页组件&#xff0c;要求包含以下功能&#xff1a;1.支持前端分页和后端分页两种模式&#xff1b;2.可自定义每页显示数量选项&#…

作者头像 李华
网站建设 2026/5/11 10:31:42

SSL连接失败?零基础快速排查指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个面向初学者的SSL问题排查向导。要求&#xff1a;1.使用简单易懂的语言解释SSL/TLS原理 2.提供图形化界面逐步引导排查 3.包含一键检测功能 4.输出带图解的解决方案。使用Tk…

作者头像 李华
网站建设 2026/5/10 4:11:43

EtherCAT vs 传统现场总线:效率对比分析

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个性能测试工具&#xff0c;用于比较EtherCAT与PROFIBUS在相同硬件环境下的通讯效率。工具应能测量数据传输延迟、带宽利用率以及错误率。使用EtherCAT协议栈和PROFIBUS协议栈…

作者头像 李华