一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

.NET基于类名约定的自动依赖注入最佳实践指南实用指南

时间:2026-09-07 14:02:01 编辑:袖梨 来源:一聚教程网

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“.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();

精准扫描范围

// 仅扫描当前程序集中的服务
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自动依赖注入内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!

您可能感兴趣的文章:
  • 实际处理时,.net core三种依赖注入方式(原生的依赖注入器,scrutor,autofac)
  • .Net WebApi中实现自动依赖注入的三种方法(最新建议)
  • 如何在.net6webapi中采用自动依赖注入
  • ASP.NET Core 依赖注入生命周期示例详解
  • .net程序开发IOC控制反转和DI依赖注入详解
  • ASP.net core采用Autofac实现泛型依赖注入
  • ASP.NET Core依赖注入详解
  • ASP.NET Core实现自动依赖注入

热门栏目