.NET基于類名約定的自動依賴注入最佳實踐指南
?? .NET基于類名約定的自動依賴注入完整指南
基于類名約定的自動依賴注入可大幅減少手動注冊服務(wù)的工作量,本文將通過清晰的結(jié)構(gòu)、美觀的排版和豐富的示例,幫助你快速掌握這一實用技術(shù)。
?? 核心特性概覽
| 特性 | 說明 |
|---|---|
| 類名約定 | 自動識別以 Service 結(jié)尾的類(不區(qū)分大小寫) |
| 接口優(yōu)先匹配 | 優(yōu)先注冊到 I{ClassName} 形式的接口(如 UserService → IUserService) |
| 多生命周期支持 | 支持 Transient/Scoped/Singleton 三種生命周期 |
| 靈活掃描控制 | 可指定任意程序集或默認掃描調(diào)用程序集 |
| 無接口自注冊 | 自動注冊未實現(xiàn)接口的類為自身類型 |
?? 完整代碼實現(xiàn)(含所有重載)
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// 依賴注入擴展方法集合(基于類名約定)
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 自動注冊以"Service"結(jié)尾的類(默認Transient生命周期)
/// </summary>
public static IServiceCollection AutoRegisterServices(
this IServiceCollection services,
Assembly assembly = null)
{
assembly ??= Assembly.GetCallingAssembly();
// 掃描規(guī)則:公共類、非抽象、非泛型、類名以Service結(jié)尾
var serviceTypes = assembly.GetTypes()
.Where(t =>
t.IsClass && !t.IsAbstract && t.IsPublic &&
!t.ContainsGenericParameters &&
t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase))
.ToArray();
// 注冊服務(wù)到容器
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
{
// 注冊到所有實現(xiàn)的接口
foreach (var @interface in interfaces)
{
services.AddTransient(@interface, implType);
}
}
}
else
{
// 無接口時注冊自身
services.AddTransient(implType);
}
}
return services;
}
/// <summary>
/// 自動注冊以"Service"結(jié)尾的類(支持自定義生命周期)
/// </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;
}
// 輔助方法:獲取服務(wù)類型(提取公共邏輯)
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();
// 輔助方法:創(chuàng)建服務(wù)描述符(提取公共邏輯)
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(掃描調(diào)用程序集)
builder.Services.AutoRegisterServices();
// 方式2:指定Scoped生命周期
builder.Services.AutoRegisterServices(ServiceLifetime.Scoped);
// 方式3:掃描指定程序集(如業(yè)務(wù)層)
var businessAssembly = Assembly.Load("MyBusinessLayer");
builder.Services.AutoRegisterServices(ServiceLifetime.Singleton, businessAssembly);
var app = builder.Build();2. 服務(wù)類示例(符合約定的實現(xiàn))
// ? 示例1:接口匹配型服務(wù)
public interface IUserService { string GetInfo(); }
public class UserService : IUserService
{
public string GetInfo() => "User Service Running";
}
// ? 示例2:多接口實現(xiàn)服務(wù)
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:無接口自注冊服務(wù)
public class DataService
{
public void ProcessData() { /* 數(shù)據(jù)處理 */ }
}
// ? 示例4:不符合約定的類(不會被注冊)
public class ServiceHelper { } // 類名不以Service結(jié)尾
public abstract class BaseService { } // 抽象類?? 擴展優(yōu)化方案(帶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. ? 緩存掃描結(jié)果提升性能
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 => /* 掃描規(guī)則 */)
.ToArray();
}
}
return _cachedServiceTypes;
}?? 最佳實踐指南
?? 混合注冊策略
- 核心服務(wù)(如DbContext)手動注冊:
services.AddDbContext<AppDbContext>(options => {...}); - 業(yè)務(wù)服務(wù)自動注冊:
builder.Services.AutoRegisterServices();
- 核心服務(wù)(如DbContext)手動注冊:
?? 精準掃描范圍
// 僅掃描當前程序集中的服務(wù) 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);
}到此這篇關(guān)于.NET基于類名約定的自動依賴注入完整指南的文章就介紹到這了,更多相關(guān).net自動依賴注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
HttpRequest的QueryString屬性 的一點認識
我們開發(fā)asp.net程序獲取QueryString時,經(jīng)常性的遇到一些url編碼問題2012-11-11
ASP.NET?Core?WebApi返回結(jié)果統(tǒng)一包裝實踐記錄
本文主要是展示了針對ASP.NET Core WeApi結(jié)果統(tǒng)一返回格式的相關(guān)操作,通過示例我們一步一步的展示了完成這一目標的不斷升級的實現(xiàn),雖然整體看起來比較簡單,但是卻承載著筆者一次又一次的思考升級2022-04-04
設(shè)置ASP.NET頁面不被緩存(客戶端/服務(wù)器端取消緩存方法)
設(shè)置頁面不被緩存:客戶端取消緩存、服務(wù)器具端取消緩存的具體實現(xiàn)代碼如下感興趣的朋友可以參考下哈,希望對大家有所幫助2013-06-06
ASP.NET網(wǎng)站的創(chuàng)建與發(fā)布過程簡析
這篇文章主要介紹了ASP.NET的創(chuàng)建與發(fā)布過程,過程實現(xiàn)很簡單,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-08-08
asp.net結(jié)合aspnetpager使用SQL2005的存儲過程分頁
項目中用到了,同事阿春寫了例子,并在實際項目中使用了,記錄下。感謝春哥的無私奉獻。2009-07-07

