.NET基于類名約定的自動依賴注入最佳實踐指南
?? .NET基于類名約定的自動依賴注入完整指南
基于類名約定的自動依賴注入可大幅減少手動注冊服務的工作量,本文將通過清晰的結(jié)構、美觀的排版和豐富的示例,幫助你快速掌握這一實用技術。
?? 核心特性概覽
特性 | 說明 |
---|---|
類名約定 | 自動識別以 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(); // 注冊服務到容器 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; } // 輔助方法:獲取服務類型(提取公共邏輯) 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)建服務描述符(提取公共邏輯) 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è)務層) var businessAssembly = Assembly.Load("MyBusinessLayer"); builder.Services.AutoRegisterServices(ServiceLifetime.Singleton, businessAssembly); var app = builder.Build();
2. 服務類示例(符合約定的實現(xiàn))
// ? 示例1:接口匹配型服務 public interface IUserService { string GetInfo(); } public class UserService : IUserService { public string GetInfo() => "User Service Running"; } // ? 示例2:多接口實現(xiàn)服務 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() { /* 數(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; }
?? 最佳實踐指南
?? 混合注冊策略
- 核心服務(如DbContext)手動注冊:
services.AddDbContext<AppDbContext>(options => {...});
- 業(yè)務服務自動注冊:
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); }
到此這篇關于.NET基于類名約定的自動依賴注入完整指南的文章就介紹到這了,更多相關.net自動依賴注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
HttpRequest的QueryString屬性 的一點認識
我們開發(fā)asp.net程序獲取QueryString時,經(jīng)常性的遇到一些url編碼問題2012-11-11ASP.NET?Core?WebApi返回結(jié)果統(tǒng)一包裝實踐記錄
本文主要是展示了針對ASP.NET Core WeApi結(jié)果統(tǒng)一返回格式的相關操作,通過示例我們一步一步的展示了完成這一目標的不斷升級的實現(xiàn),雖然整體看起來比較簡單,但是卻承載著筆者一次又一次的思考升級2022-04-04設置ASP.NET頁面不被緩存(客戶端/服務器端取消緩存方法)
設置頁面不被緩存:客戶端取消緩存、服務器具端取消緩存的具體實現(xiàn)代碼如下感興趣的朋友可以參考下哈,希望對大家有所幫助2013-06-06ASP.NET網(wǎng)站的創(chuàng)建與發(fā)布過程簡析
這篇文章主要介紹了ASP.NET的創(chuàng)建與發(fā)布過程,過程實現(xiàn)很簡單,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-08-08asp.net結(jié)合aspnetpager使用SQL2005的存儲過程分頁
項目中用到了,同事阿春寫了例子,并在實際項目中使用了,記錄下。感謝春哥的無私奉獻。2009-07-07