🚀 .NET基于类名约定的自动依赖注入完整指南
基于类名约定的自动依赖注入可大幅减少手动注册服务的工作量,本文将通过清晰的结构、美观的排版和丰富的示例,帮助你快速掌握这一实用技术。
🌈 核心特性概览
特性 | 说明 |
---|---|
类名约定 | 自动识别以 Service 结尾的类(不区分大小写) |
接口优先匹配 | 优先注册到 I{ClassName} 形式的接口(如 UserService → IUserService ) |
多生命周期支持 | 支持 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;
}
📝 最佳实践指南
📦 混合注册策略
- 核心服务(如DbContext)手动注册:
services.AddDbContext<AppDbContext>(options => {...});
- 业务服务自动注册:
builder.Services.AutoRegisterServices();
- 核心服务(如DbContext)手动注册:
🔍 精准扫描范围
// 仅扫描当前程序集中的服务 builder.Services.AutoRegisterServices(typeof(UserService).Assembly);
✅ 单元测试验证
[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); }