.NET基于類名約定的自動(dòng)依賴注入最佳實(shí)踐指南
?? .NET基于類名約定的自動(dòng)依賴注入完整指南
基于類名約定的自動(dòng)依賴注入可大幅減少手動(dòng)注冊服務(wù)的工作量,本文將通過清晰的結(jié)構(gòu)、美觀的排版和豐富的示例,幫助你快速掌握這一實(shí)用技術(shù)。
?? 核心特性概覽
| 特性 | 說明 |
|---|---|
| 類名約定 | 自動(dòng)識別以 Service 結(jié)尾的類(不區(qū)分大小寫) |
| 接口優(yōu)先匹配 | 優(yōu)先注冊到 I{ClassName} 形式的接口(如 UserService → IUserService) |
| 多生命周期支持 | 支持 Transient/Scoped/Singleton 三種生命周期 |
| 靈活掃描控制 | 可指定任意程序集或默認(rèn)掃描調(diào)用程序集 |
| 無接口自注冊 | 自動(dòng)注冊未實(shí)現(xiàn)接口的類為自身類型 |
?? 完整代碼實(shí)現(xiàn)(含所有重載)
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// 依賴注入擴(kuò)展方法集合(基于類名約定)
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 自動(dòng)注冊以"Service"結(jié)尾的類(默認(rèn)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
{
// 注冊到所有實(shí)現(xiàn)的接口
foreach (var @interface in interfaces)
{
services.AddTransient(@interface, implType);
}
}
}
else
{
// 無接口時(shí)注冊自身
services.AddTransient(implType);
}
}
return services;
}
/// <summary>
/// 自動(dòng)注冊以"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);
}
// 返回第一個(gè)接口(避免注冊多個(gè)描述符)
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:默認(rèn)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ù)類示例(符合約定的實(shí)現(xiàn))
// ? 示例1:接口匹配型服務(wù)
public interface IUserService { string GetInfo(); }
public class UserService : IUserService
{
public string GetInfo() => "User Service Running";
}
// ? 示例2:多接口實(shí)現(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 { } // 抽象類?? 擴(kuò)展優(yōu)化方案(帶emoji標(biāo)記)
1. ?? 基于類名的生命周期自動(dòng)識別
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. ?? 特性標(biāo)記增強(qiáng)控制
[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;
}?? 最佳實(shí)踐指南
?? 混合注冊策略
- 核心服務(wù)(如DbContext)手動(dòng)注冊:
services.AddDbContext<AppDbContext>(options => {...}); - 業(yè)務(wù)服務(wù)自動(dòng)注冊:
builder.Services.AutoRegisterServices();
- 核心服務(wù)(如DbContext)手動(dòng)注冊:
?? 精準(zhǔn)掃描范圍
// 僅掃描當(dāng)前程序集中的服務(wù) builder.Services.AutoRegisterServices(typeof(UserService).Assembly);
? 單元測試驗(yàn)證
[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基于類名約定的自動(dòng)依賴注入完整指南的文章就介紹到這了,更多相關(guān).net自動(dòng)依賴注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
動(dòng)態(tài)ItemTemplate的實(shí)現(xiàn)(譯) - item,template
動(dòng)態(tài)ItemTemplate的實(shí)現(xiàn)(譯) - item,template...2007-02-02
在asp.net中操作sql server數(shù)據(jù)庫的一些小技巧
在asp.net中操作sql server數(shù)據(jù)庫的一些小技巧...2006-09-09
無法將類型為“System.Web.UI.WebControls.HiddenField”的對象強(qiáng)制轉(zhuǎn)換為類型的錯(cuò)誤的解
無法將類型為“System.Web.UI.WebControls.HiddenField”的對象強(qiáng)制轉(zhuǎn)換為類型的錯(cuò)誤的解決方法2011-12-12
asp.net 按指定模板導(dǎo)出word,pdf實(shí)例代碼
這篇文章介紹了asp.net 按指定模板導(dǎo)出word,pdf實(shí)例代碼,有需要的朋友可以參考一下2013-09-09
.NET Core中創(chuàng)建和使用NuGet包的示例代碼
這篇文章主要介紹了.NET Core中創(chuàng)建和使用NuGet包的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
.NET實(shí)現(xiàn)文件跨服務(wù)器上傳下載的方法
這篇文章主要給大家介紹了.NET文件如何實(shí)現(xiàn)跨服務(wù)器上傳下載的方法,文中通過圖片介紹的很詳細(xì),相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們可以跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2016-12-12
ASP.NET?Core?WebApi返回結(jié)果統(tǒng)一包裝實(shí)踐記錄
本文主要是展示了針對ASP.NET Core WeApi結(jié)果統(tǒng)一返回格式的相關(guān)操作,通過示例我們一步一步的展示了完成這一目標(biāo)的不斷升級的實(shí)現(xiàn),雖然整體看起來比較簡單,但是卻承載著筆者一次又一次的思考升級2022-04-04
asp.net Javascript獲取CheckBoxList的value
最近在做一個(gè)BS的小項(xiàng)目,記得自己搞asp.net的時(shí)候,還是兩年以前,大部分的東西只是有點(diǎn)印象,忘得差不多了,所以這次也算是溫習(xí)的過程吧,一邊學(xué)習(xí),一邊趕工,呵呵呵。。。。2009-12-12
創(chuàng)建一個(gè)ASP.NET MVC5項(xiàng)目的實(shí)現(xiàn)方法(圖文)
這篇文章主要介紹了創(chuàng)建一個(gè)ASP.NET MVC 5項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

