.NET基于类名约定的自动依赖注入完整指南

🚀 .NET基于类名约定的自动依赖注入完整指南

基于类名约定的自动依赖注入可大幅减少手动注册服务的工作量,本文将通过清晰的结构、美观的排版和丰富的示例,帮助你快速掌握这一实用技术。

🌈 核心特性概览

特性说明
类名约定自动识别以 Service 结尾的类(不区分大小写)
接口优先匹配优先注册到 I{ClassName} 形式的接口(如 UserServiceIUserService
多生命周期支持支持 Transient/Scoped/Singleton 三种生命周期
灵活扫描控制可指定任意程序集或默认扫描调用程序集
无接口自注册自动注册未实现接口的类为自身类型

📦 完整代码实现(含所有重载)

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;/// <summary>
/// 依赖注入扩展方法集合(基于类名约定)
/// </summary>
public static class ServiceCollectionExtensions
{/// <summary>/// 自动注册以"Service"结尾的类(默认Transient生命周期)/// </summary>public static IServiceCollection AutoRegisterServices(this IServiceCollection services,Assembly assembly = null){assembly ??= Assembly.GetCallingAssembly();// 扫描规则:公共类、非抽象、非泛型、类名以Service结尾var serviceTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.IsPublic &&!t.ContainsGenericParameters &&t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase)).ToArray();// 注册服务到容器foreach (var implType in serviceTypes){var interfaces = implType.GetInterfaces();if (interfaces.Any()){// 匹配I{ClassName}接口(如UserService→IUserService)var matchingInterface = interfaces.FirstOrDefault(i => i.Name == "I" + implType.Name);if (matchingInterface != null){services.AddTransient(matchingInterface, implType);}else{// 注册到所有实现的接口foreach (var @interface in interfaces){services.AddTransient(@interface, implType);}}}else{// 无接口时注册自身services.AddTransient(implType);}}return services;}/// <summary>/// 自动注册以"Service"结尾的类(支持自定义生命周期)/// </summary>public static IServiceCollection AutoRegisterServices(this IServiceCollection services,ServiceLifetime lifetime,Assembly assembly = null){assembly ??= Assembly.GetCallingAssembly();var serviceTypes = GetServiceTypes(assembly);foreach (var implType in serviceTypes){var interfaces = implType.GetInterfaces();var descriptor = CreateServiceDescriptor(implType, interfaces, lifetime);services.Add(descriptor);}return services;}// 辅助方法:获取服务类型(提取公共逻辑)private static Type[] GetServiceTypes(Assembly assembly) =>assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.IsPublic &&!t.ContainsGenericParameters &&t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase)).ToArray();// 辅助方法:创建服务描述符(提取公共逻辑)private static ServiceDescriptor CreateServiceDescriptor(Type implType, Type[] interfaces, ServiceLifetime lifetime){if (interfaces.Any()){var matchingInterface = interfaces.FirstOrDefault(i => i.Name == "I" + implType.Name);if (matchingInterface != null){return new ServiceDescriptor(matchingInterface, implType, lifetime);}// 返回第一个接口(避免注册多个描述符)return new ServiceDescriptor(interfaces[0], implType, lifetime);}return new ServiceDescriptor(implType, implType, lifetime);}
}

🚀 使用示例(清晰排版)

1. 在ASP.NET Core中注册(Program.cs

var builder = WebApplication.CreateBuilder(args);// 方式1:默认Transient(扫描调用程序集)
builder.Services.AutoRegisterServices();// 方式2:指定Scoped生命周期
builder.Services.AutoRegisterServices(ServiceLifetime.Scoped);// 方式3:扫描指定程序集(如业务层)
var businessAssembly = Assembly.Load("MyBusinessLayer");
builder.Services.AutoRegisterServices(ServiceLifetime.Singleton, businessAssembly);var app = builder.Build();

2. 服务类示例(符合约定的实现)

// ✅ 示例1:接口匹配型服务
public interface IUserService { string GetInfo(); }
public class UserService : IUserService 
{public string GetInfo() => "User Service Running";
}// ✅ 示例2:多接口实现服务
public interface IAuthService { void Login(); }
public interface ILogService { void WriteLog(string msg); }
public class AuthService : IAuthService, ILogService 
{public void Login() { /* 登录逻辑 */ }public void WriteLog(string msg) { /* 日志逻辑 */ }
}// ✅ 示例3:无接口自注册服务
public class DataService 
{public void ProcessData() { /* 数据处理 */ }
}// ❌ 示例4:不符合约定的类(不会被注册)
public class ServiceHelper { }        // 类名不以Service结尾
public abstract class BaseService { } // 抽象类

🔧 扩展优化方案(带emoji标记)

1. 🌐 基于类名的生命周期自动识别

private static ServiceLifetime GetLifetimeFromName(string className)
{if (className.Contains("Singleton", StringComparison.OrdinalIgnoreCase))return ServiceLifetime.Singleton;if (className.Contains("Scoped", StringComparison.OrdinalIgnoreCase))return ServiceLifetime.Scoped;return ServiceLifetime.Transient;
}

2. 📌 特性标记增强控制

[AttributeUsage(AttributeTargets.Class)]
public class AutoRegisterAttribute : Attribute 
{public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient;public bool IsEnabled { get; set; } = true;
}

3. ⚡ 缓存扫描结果提升性能

private static readonly object LockObj = new();
private static Type[] _cachedServiceTypes;private static Type[] GetCachedServiceTypes(Assembly assembly)
{if (_cachedServiceTypes == null){lock (LockObj){_cachedServiceTypes = assembly.GetTypes().Where(t => /* 扫描规则 */).ToArray();}}return _cachedServiceTypes;
}

📝 最佳实践指南

  1. 📦 混合注册策略

    • 核心服务(如DbContext)手动注册:
      services.AddDbContext<AppDbContext>(options => {...});
    • 业务服务自动注册:
      builder.Services.AutoRegisterServices();
  2. 🔍 精准扫描范围

    // 仅扫描当前程序集中的服务
    builder.Services.AutoRegisterServices(typeof(UserService).Assembly);
    
  3. ✅ 单元测试验证

    [Fact]
    public void Should_Resolve_Service_By_Convention()
    {var services = new ServiceCollection();services.AutoRegisterServices(typeof(IAuthService).Assembly);var provider = services.BuildServiceProvider();var service = provider.GetService<IAuthService>();Assert.NotNull(service);
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87868.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis各数据结构的详细使用和使用场景

Redis各数据结构的详细使用 大家好&#xff01;今天我们来聊聊Redis这个强大的内存数据库。就像我们生活中的工具箱一样&#xff0c;Redis提供了多种"工具"&#xff08;数据结构&#xff09;来帮助我们解决不同的问题。有些工具像螺丝刀&#xff08;字符串&#xff…

MSYS2 环境下 Python 开发配置(结合 PyCharm)使用笔记

【笔记】MSYS2 的 MinGW64 环境中正确安装 Python 相关环境管理工具 &#xff08;Poetry、Virtualenv、Pipenv 和 UV&#xff09;-CSDN博客 MSYS2 环境配置与 Python 项目依赖管理笔记_msys更新python-CSDN博客 【技术笔记】MSYS2 指定 Python 版本安装方案_pacman -u 安装指定…

Python爬虫实战:研究Splinter相关技术

1. 引言 1.1 研究背景与意义 随着 Web 2.0 技术的发展,现代网页越来越多地采用 JavaScript 动态生成内容。传统爬虫通过直接请求 HTML 页面的方式,无法获取这些动态渲染的内容,导致爬取数据不完整。据统计,全球前 1000 名网站中,超过 70% 的页面包含动态加载内容 。Spli…

大气商务工作汇报总结PPT模版分享

蓝色商务工作总结PPT模版&#xff0c;莫兰迪工作总结PPT模版&#xff0c;年中工作汇报PPT模版&#xff0c;简约工作汇报PPT模版&#xff0c;上半年工作总结PPT模版&#xff0c;极简工作汇报PPT模版&#xff0c;欧美简约PPT模版&#xff0c;大气商务通用PPT模版&#xff0c;团队…

5G modem开发

链接文章&#xff1a;https://zhuanlan.zhihu.com/p/709130546 OpenHarmony RIL架构 链接文章&#xff1a;https://blog.csdn.net/weixin_42571280/article/details/148566029 在移动通信设备中&#xff0c;无线接口层&#xff08;Radio Interface Layer&#xff0c;简称RIL&…

Gartner《AI-Driven Methods for Cost-Efficiency》学习心得

一、背景介绍 在当前经济形势下,企业面临着成本上升与收入增长放缓的双重压力。Gartner 的这份报告指出,大多数企业对 AI 的投资主要集中在提升用户生产力方面,但短期内投资回报率有限。鉴于经济的不确定性以及成本压力,尤其是生成式 AI(GenAI)技术,若应用于财务效率和…

人脸识别技术是自动化还是智能化?

人脸识别技术兼具自动化与智能化的双重特性。它通过自动采集图像、预处理图像、提取特征以及进行识别比对等操作&#xff0c;实现了高效且无需人工干预的识别流程&#xff0c;展现出强大的自动化能力。同时&#xff0c;它还具备自适应学习能力&#xff0c;能够根据新的数据和场…

树结构的实际应用之堆排序

树结构的实际应用之堆排序 基本介绍 堆排序是利用堆这种数据结构设计而成的一种排序算法&#xff0c;堆排序是一种选择排序&#xff0c;它的最坏&#xff0c;最好&#xff0c;平均时间复杂度为O(logn)&#xff0c;它也是不稳定排序。堆是具有以下性质的完全二叉树&#xff1a;…

用OBS Studio录制WAV音频,玩转语音克隆和文本转语音!

言简意赅的讲解OBS Studio解决的痛点 随着AI技术的快速发展&#xff0c;语音克隆与文本生成语音技术越来越受欢迎。无论你想要制作个人虚拟主播&#xff0c;还是给自媒体视频配音&#xff0c;拥有高质量的原始音频都是关键。本文详细教你使用免费且功能强大的软件——OBS Stud…

LangChain-5-agent

概述 Agent 是一种能够基于接收到的输入&#xff0c;利用自身的决策逻辑和可用的工具&#xff0c;动态地规划并执行一系列操作&#xff0c;以达成特定任务的程序或系统。它在与外界交互过程中&#xff0c;会根据实时情况灵活调整策略&#xff0c;而不是按照固定的预设流程执行…

操作系统进程与线程核心知识全览

本博客&#xff0c;根据王道所学。以下为第二章节知识点&#xff1a; 进程的概念、组成、状态与其转换、进程间通信、信号&#xff1b; 单/多线程模型、线程管理、调度时机的切换、调度的目标、调度算法、多处理机调度&#xff1b; 同步与互斥、进程互斥的软硬件实现方法、信号…

C++中类型转换操作符知识介绍

文章目录 **一、类型转换操作符的语法与定义****二、工作原理****三、示例&#xff1a;基本类型转换****四、示例&#xff1a;转换为自定义类型****五、与构造函数的对比****六、注意事项****七、应用场景****八、与 C 其他类型转换的关系****九、总结** 在C中&#xff0c;类型…

2048小游戏C++板来啦!

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 大家好呀&#xff0c;我是PingdiGuo_guo&#xff0c;今天我们来学习如何用C编写一个2048小游戏。 文章目录 1.2048的规则 2.步骤实现 2.1: 初始化游戏界面 2.1.1知识点 2.1.2: 创建游戏界面 2.2: 随机…

TensorFlow深度学习实战——Transformer变体模型

TensorFlow深度学习实战——Transformer变体模型 0. 前言1. BERT2. GPT-23. GPT-34. Reformer5. BigBird6. Transformer-XL7. XLNet8. RoBERTa9. ALBERT10. StructBERT11. T5 和 MUM12. ELECTRA13. DeBERTa14. 进化 Transformer 和 MEENA15. LaMDA16. Switch Transformer17. RE…

还原自动驾驶的“前世今生”:用 Python 实现数据记录与回放系统

还原自动驾驶的“前世今生”:用 Python 实现数据记录与回放系统 你有没有想过这样一个场景: 一辆自动驾驶测试车,在街头拐了个弯,却突然急刹。测试员一脸懵,研发团队问:“数据记录了吗?” 他摊摊手:“系统当时没挂上录制……” 对不起,重测吧。 这不是段子,而是我在…

access和excel用vba进行辅助办公软件开发

1、access用vba创建子窗口child查询 出现这个报错的时候&#xff0c;一般是用vba通过ado.connection连接&#xff0c;没有绑定数据源造成的&#xff1a; 先绑定再使用 Me.Child2.SourceObject "表.资产管理" 连接数据源 Me.Child2.Form.RecordSource strSql …

Nginx+tomcat集群

Nginxtomcat集群 一、Nginx 简介 1.1 定义 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器&#xff0c;同时支持 IMAP/POP3/SMTP 服务。由俄罗斯工程师伊戈尔・赛索耶夫开发&#xff0c;于 2004 年首次公开发布&#xff0c;基于 BSD-like 协议&#xff0c;代码开源且免费…

RPC - 客户端注册和发现模块

registryMethod 函数详解&#xff1a; 函数目的 registryMethod 是 Provider 类的核心方法&#xff0c;用于向服务注册中心注册服务。注册成功后&#xff0c;服务注册中心会更新内部的服务映射表&#xff0c;建立服务名称到提供者地址的映射关系。 执行流程示例 场景: 多米…

leetcode332.重新安排行程:优先队列与DFS实现欧拉路径的行程规划

一、题目深度解析与行程规划本质 题目描述 给定一个机票的字符串二维数组 tickets&#xff0c;每个元素是 [from, to] 的形式&#xff0c;表示从 from 到 to 的机票。要求找出从 JFK 出发的行程&#xff0c;且必须使用所有机票&#xff0c;若存在多种可能的行程&#xff0c;返…

1.21SQLCipher 简介

SQLCipher 是一个基于 SQLite 的扩展&#xff0c;提供了透明的数据库加密功能。与普通 SQLite 不同&#xff0c;SQLCipher 在数据写入磁盘前自动加密&#xff0c;读取时自动解密&#xff0c;无需开发者手动处理加密逻辑。这使得它非常适合移动应用、桌面应用等需要本地数据加密…