吴大仙 發表於 2025-6-25 09:29:34

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">🚀 .NET基于类名约定的自动依赖注入完整指南</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">🌈 核心特性概览</a></li><li><a href="#_lab2_0_1">📦 完整代码实现(含所有重载)</a></li><li><a href="#_lab2_0_2">🚀 使用示例(清晰排版)</a></li><ul class="third_class_ul"><li><a href="#_label3_0_2_0">1. 在ASP.NET Core中注册(Program.cs)</a></li><li><a href="#_label3_0_2_1">2. 服务类示例(符合约定的实现)</a></li></ul><li><a href="#_lab2_0_3">🔧 扩展优化方案(带emoji标记)</a></li><ul class="third_class_ul"><li><a href="#_label3_0_3_2">1. 🌐 基于类名的生命周期自动识别</a></li><li><a href="#_label3_0_3_3">2. 📌 特性标记增强控制</a></li><li><a href="#_label3_0_3_4">3. ⚡ 缓存扫描结果提升性能</a></li></ul><li><a href="#_lab2_0_4">📝 最佳实践指南</a></li><ul class="third_class_ul"></ul></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>🚀 .NET基于类名约定的自动依赖注入完整指南</h2>
<p>基于类名约定的自动依赖注入可大幅减少手动注册服务的工作量,本文将通过清晰的结构、美观的排版和丰富的示例,帮助你快速掌握这一实用技术。</p>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>🌈 核心特性概览</h3>
<table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td><strong>类名约定</strong></td><td>自动识别以 <code>Service</code> 结尾的类(不区分大小写)</td></tr><tr><td><strong>接口优先匹配</strong></td><td>优先注册到 <code>I{ClassName}</code> 形式的接口(如 <code>UserService</code> &rarr; <code>IUserService</code>)</td></tr><tr><td><strong>多生命周期支持</strong></td><td>支持 <code>Transient</code>/<code>Scoped</code>/<code>Singleton</code> 三种生命周期</td></tr><tr><td><strong>灵活扫描控制</strong></td><td>可指定任意程序集或默认扫描调用程序集</td></tr><tr><td><strong>无接口自注册</strong></td><td>自动注册未实现接口的类为自身类型</td></tr></tbody></table>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>📦 完整代码实现(含所有重载)</h3>
<div class="jb51code"><pre class="brush:csharp;">using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
/// &lt;summary&gt;
/// 依赖注入扩展方法集合(基于类名约定)
/// &lt;/summary&gt;
public static class ServiceCollectionExtensions
{
    /// &lt;summary&gt;
    /// 自动注册以"Service"结尾的类(默认Transient生命周期)
    /// &lt;/summary&gt;
    public static IServiceCollection AutoRegisterServices(
      this IServiceCollection services,
      Assembly assembly = null)
    {
      assembly ??= Assembly.GetCallingAssembly();
      // 扫描规则:公共类、非抽象、非泛型、类名以Service结尾
      var serviceTypes = assembly.GetTypes()
            .Where(t =&gt;
                t.IsClass &amp;&amp; !t.IsAbstract &amp;&amp; t.IsPublic &amp;&amp;
                !t.ContainsGenericParameters &amp;&amp;
                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 =&gt;
                  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;
    }
    /// &lt;summary&gt;
    /// 自动注册以"Service"结尾的类(支持自定义生命周期)
    /// &lt;/summary&gt;
    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) =&gt;
      assembly.GetTypes()
            .Where(t =&gt;
                t.IsClass &amp;&amp; !t.IsAbstract &amp;&amp; t.IsPublic &amp;&amp;
                !t.ContainsGenericParameters &amp;&amp;
                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 =&gt;
                i.Name == "I" + implType.Name);
            if (matchingInterface != null)
            {
                return new ServiceDescriptor(matchingInterface, implType, lifetime);
            }
            // 返回第一个接口(避免注册多个描述符)
            return new ServiceDescriptor(interfaces, implType, lifetime);
      }
      return new ServiceDescriptor(implType, implType, lifetime);
    }
}</pre></div>
<p class="maodian"><a name="_lab2_0_2"></a></p><h3>🚀 使用示例(清晰排版)</h3>
<p class="maodian"><a name="_label3_0_2_0"></a></p><h4>1. 在ASP.NET Core中注册(<code>Program.cs</code>)</h4>
<div class="jb51code"><pre class="brush:csharp;">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();</pre></div>
<p class="maodian"><a name="_label3_0_2_1"></a></p><h4>2. 服务类示例(符合约定的实现)</h4>
<div class="jb51code"><pre class="brush:csharp;">// ✅ 示例1:接口匹配型服务
public interface IUserService { string GetInfo(); }
public class UserService : IUserService
{
    public string GetInfo() =&gt; "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 { } // 抽象类</pre></div>
<p class="maodian"><a name="_lab2_0_3"></a></p><h3>🔧 扩展优化方案(带emoji标记)</h3>
<p class="maodian"><a name="_label3_0_3_2"></a></p><h4>1. 🌐 基于类名的生命周期自动识别</h4>
<div class="jb51code"><pre class="brush:csharp;">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;
}</pre></div>
<p class="maodian"><a name="_label3_0_3_3"></a></p><h4>2. 📌 特性标记增强控制</h4>
<div class="jb51code"><pre class="brush:csharp;">
public class AutoRegisterAttribute : Attribute
{
    public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient;
    public bool IsEnabled { get; set; } = true;
}</pre></div>
<p class="maodian"><a name="_label3_0_3_4"></a></p><h4>3. ⚡ 缓存扫描结果提升性能</h4>
<div class="jb51code"><pre class="brush:csharp;">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 =&gt; /* 扫描规则 */)
                .ToArray();
      }
    }
    return _cachedServiceTypes;
}</pre></div>
<p class="maodian"><a name="_lab2_0_4"></a></p><h3>📝 最佳实践指南</h3>
<ol><li><p><strong>📦 混合注册策略</strong></p>
<ul><li>核心服务(如DbContext)手动注册:<br /><code>services.AddDbContext&lt;AppDbContext&gt;(options =&gt; {...});</code></li><li>业务服务自动注册:<br /><code>builder.Services.AutoRegisterServices();</code></li></ul></li></ol>
<p><strong>🔍 精准扫描范围</strong></p>
<div class="jb51code"><pre class="brush:csharp;">// 仅扫描当前程序集中的服务
builder.Services.AutoRegisterServices(typeof(UserService).Assembly);</pre></div>
<p><strong>✅ 单元测试验证</strong></p>
<div class="jb51code"><pre class="brush:csharp;">
public void Should_Resolve_Service_By_Convention()
{
    var services = new ServiceCollection();
    services.AutoRegisterServices(typeof(IAuthService).Assembly);
    var provider = services.BuildServiceProvider();
    var service = provider.GetService&lt;IAuthService&gt;();
    Assert.NotNull(service);
}</pre></div>
頁: [1]
查看完整版本: .NET基于类名约定的自动依赖注入最佳实践指南