C#依赖注入进阶:特性驱动的自动服务注册,让你的代码更优雅

C#依赖注入进阶:特性驱动的自动服务注册,让你的代码更优雅

编码文章call10242025-08-19 13:35:344A+A-

你是否还在为每次新增服务都要手动在 Program.cs 中添加注册代码而烦恼?随着项目规模的扩大,服务注册的代码越来越冗长,维护成本也越来越高。

今天我们来探讨一个更优雅的解决方案: 基于特性(Attribute)的自动服务注册 ,让你彻底告别手动注册的烦恼,让代码更简洁、更易维护。 这也是闲来在java spring时发现它有些好玩的地方,也就在.net 是实现着玩。

问题分析:传统服务注册的痛点

传统方式的问题

在传统的.NET依赖注入中,我们需要在 Program.cs Startup.cs 中逐一注册每个服务:

// 传统注册方式 - 冗长且容易遗漏
services.AddScoped();
services.AddSingleton();
services.AddTransient();
services.AddScoped();
// ... 50+个服务的注册代码

痛点总结:

解决方案:特性驱动的自动注册

核心思路

通过自定义特性标记需要注册的服务,在启动时通过反射自动扫描并注册,实现 "声明式" 的服务注册。

代码实战

1 定义自动注册特性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace AppDIAttr
{
///
/// 服务注册特性 - 支持自定义生命周期
///
[AttributeUsage(AttributeTargets.Class)]
publicclass AutoRegisterAttribute : Attribute
{
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Scoped;

public AutoRegisterAttribute(ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
Lifetime = lifetime;
}
}
}

设计亮点:

2 实现自动注册扩展方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace AppDIAttr
{
///
/// 增强版自动服务注册 - 支持特性控制
///
publicstaticclass ServiceCollectionExtensions
{

public static IServiceCollection AddAutoRegisteredServices(
this IServiceCollection services,
params Assembly[] assemblies)

{
foreach (var assembly in assemblies)
{
// 只注册标记了AutoRegister特性的类
var typesToRegister = assembly.GetTypes()
.Where(type => type.IsClass
&& !type.IsAbstract
&& type.GetCustomAttribute() != null);

foreach (var type in typesToRegister)
{
var attribute = type.GetCustomAttribute();
var interfaces = type.GetInterfaces().Where(i => !i.IsGenericTypeDefinition);

// 根据特性指定的生命周期注册服务
if (interfaces.Any())
{
foreach (var @interface in interfaces)
{
RegisterService(services, @interface, type, attribute.Lifetime);
}
}
else
{
RegisterService(services, type, type, attribute.Lifetime);
}
}
}

return services;
}

private static void RegisterService(IServiceCollection services, Type serviceType,
Type implementationType, ServiceLifetime lifetime)

{
switch (lifetime)
{
case ServiceLifetime.Singleton:
services.AddSingleton(serviceType, implementationType);
break;
case ServiceLifetime.Transient:
services.AddTransient(serviceType, implementationType);
break;
default:
services.AddScoped(serviceType, implementationType);
break;
}

Console.WriteLine($" 已注册 {lifetime} 服务: {serviceType.Name} -> {implementationType.Name}");
}
}
}

核心逻辑解析:

3 服务实现示例

/// 
/// 缓存服务 - 单例模式,全局共享
///
[AutoRegister(ServiceLifetime.Singleton)]
publicclass CacheService : ICacheService
{
private readonly Dictionary<string, object> _cache = new();
private readonly ILogger _logger;

public CacheService(ILogger logger)
{
_logger = logger;
_logger.LogInformation(" CacheService 实例已创建 (Singleton)");
}

public void Set(string key, object value)
{
_cache[key] = value;
_logger.LogInformation($" 缓存已设置: {key}");
}

public T Get(string key)
{
if (_cache.TryGetValue(key, out var value))
{
_logger.LogInformation($" 缓存命中: {key}");
return (T)value;
}
_logger.LogInformation($" 缓存未命中: {key}");
returndefault(T);
}

public void Remove(string key)
{
_cache.Remove(key);
_logger.LogInformation($" 缓存已删除: {key}");
}
}

///
/// 数据仓储服务 - 请求作用域
///
[AutoRegister(ServiceLifetime.Scoped)]
publicclass DataRepository : IDataRepository
{
private readonly ILogger _logger;
privatestatic readonly List _users = new(); // 模拟数据库

public DataRepository(ILogger logger)
{
_logger = logger;
_logger.LogInformation(" DataRepository 实例已创建 (Scoped)");
}

// 实现具体的数据操作方法...
}

///
/// 邮件服务 - 瞬时模式,每次使用创建新实例
///
[AutoRegister(ServiceLifetime.Transient)]
publicclass EmailService : IEmailService
{
private readonly ILogger _logger;

public EmailService(ILogger logger)
{
_logger = logger;
_logger.LogInformation(" EmailService 实例已创建 (Transient)");
}

// 实现邮件发送逻辑...
}

4 在启动类中使用

using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AppDIAttr
{
internal class Program
{

static async Task Main(string[] args)
{
Console.OutputEncoding=System.Text.Encoding.UTF8;
Console.WriteLine(" 自动服务注册演示程序");
Console.WriteLine("==========================\n");

// 创建Host Builder
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// 自动注册所有标记了AutoRegister特性的服务
Console.WriteLine(" 开始自动服务注册:");
services.AddAutoRegisteredServices(Assembly.GetExecutingAssembly());

// 注册应用程序主类
services.AddScoped();

Console.WriteLine("\n 服务注册完成!\n");
})
.Build();

// 创建作用域并运行应用程序
using var scope = host.Services.CreateScope();
var app = scope.ServiceProvider.GetRequiredService();

await app.RunAsync();

Console.WriteLine("\n 程序执行完成,按任意键退出...");
Console.ReadKey();
}
}
}

实际应用场景

适用场景

不适用场景

常见坑点提醒

1. 程序集扫描范围

//  错误:只扫描当前程序集
services.AddAutoRegisteredServices(Assembly.GetExecutingAssembly());

// 正确:扫描所有相关程序集
services.AddAutoRegisteredServices(
Assembly.GetExecutingAssembly(),
Assembly.GetAssembly(typeof(SomeServiceInOtherAssembly))
);

2. 接口命名约定

//  注意:确保接口和实现类的命名符合约定
public interface IUserService { }
public class UserService : IUserService { } // 标准命名

public interface IDataAccess { }
public class SqlDataAccess : IDataAccess { } // 也可以

3. 泛型接口处理

//  当前实现不支持泛型接口,需要特殊处理
public interface IRepository { }
public class UserRepository : IRepository { } // 需要手动注册

总结

通过特性驱动的自动服务注册,我们实现了:

  1. 开发效率提升
    一行代码替代数十行注册代码
  2. 错误率降低
    避免忘记注册服务的运行时错误
  3. 维护性增强
    服务定义和注册逻辑紧密结合

这种方式特别适合 中大型项目 团队协作开发 ,让你的C#项目更加优雅和易维护。


互动话题:

  1. 你在实际项目中是如何管理服务注册的?
  2. 对于这种自动注册方式,你觉得还有哪些可以改进的地方?

觉得这个技巧有用吗? 点赞支持 转发给更多.NET开发同行 吧!让我们一起写出更优雅的C#代码!

收藏本文 ,下次项目重构时直接使用这套解决方案!

如果你正在从事上位机、自动化、IT、机器视觉、物联网(IOT)项目或数字化转型方面的工作,欢迎加入我的微信圈子!在这里,我们不仅可以轻松畅聊最新技术动态和行业趋势,还能够在技术问题上互相帮助和支持。我会尽量利用我的知识和经验来帮助你解决问题,当然也期待从大家的专业见解中学习和成长。无论你是新手还是老鸟,期待与志同道合的朋友交流心得,一起进步!

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4