Scrutor:.NET 依赖注入自动化的优雅实现 在现代 .NET 开发中依赖注入DI已经成为标配功能。手动注册每一个服务不仅代码繁琐、重复度高还容易出现漏注册、错注册的问题给项目维护带来负担。Scrutor是一款轻量的 NuGet 扩展库基于约定优于配置的思想让 .NET 依赖注入实现全自动注册大幅简化开发流程。本文结合实际项目经验详解 Scrutor 的核心用法、场景适配和最佳实践帮你快速落地自动化 DI。一、什么是 ScrutorScrutor 由开发者 Kristian Hellang 打造是 ASP.NET Core 原生 DI 容器的扩展库。它不改变原生 DI 的核心逻辑只提供程序集扫描、批量筛选、自动注册的能力让服务注册从手动编写变成自动匹配。核心优势告别重复代码统一注册规则降低维护成本严格遵循 DRY 原则保证全项目服务注册逻辑一致完美支持 Scoped/Transient/Singleton 三种生命周期过滤、命名、继承、泛型等灵活配置适配各类项目结构无侵入式设计兼容原生 DI可随时切换回手动注册二、首选推荐标记接口模式企业级最佳实践这是我在实际项目中长期使用的方案简洁、易维护、可读性强也是中大型项目的最优选择。2.1 定义生命周期标记接口先创建三个空接口仅用于标记服务的生命周期无任何业务逻辑/// summary /// Scoped 生命周期标记接口 /// /summary public interface IScopedDependency { } /// summary /// Transient 生命周期标记接口 /// /summary public interface ITransientDependency { } /// summary /// Singleton 生命周期标记接口 /// /summary public interface ISingletonDependency { }2.2 封装统一自动注入配置写一个扩展方法一次性完成所有标记服务的扫描注册后续无需修改public static class DependencyInjectionExtensions { public static IServiceCollection AddAutoDependencyInjection(this IServiceCollection services) { // 扫描项目所有程序集自动匹配标记接口的服务 services.Scan(scan scan .FromApplicationDependencies() // 注册 Scoped 服务 .AddClasses(classes classes.AssignableToIScopedDependency()) .AsImplementedInterfaces() .WithScopedLifetime() // 注册 Transient 服务 .AddClasses(classes classes.AssignableToITransientDependency()) .AsImplementedInterfaces() .WithTransientLifetime() // 注册 Singleton 服务 .AddClasses(classes classes.AssignableToISingletonDependency()) .AsImplementedInterfaces() .WithSingletonLifetime() ); return services; } }2.3 极简使用方式新增服务时只需要实现对应的标记接口无需编写任何注册代码// 业务接口继承 IScopedDependency标记生命周期 public interface IUserService : IScopedDependency { TaskUserInfo GetUserByIdAsync(Guid userId); } // 实现类无需额外配置 public class UserService : IUserService { public TaskUserInfo GetUserByIdAsync(Guid userId) { // 业务实现 return Task.FromResult(new UserInfo()); } }推荐理由零配置新增服务只加一个接口完全不用管注册逻辑高可读一眼就能看出服务的生命周期代码自解释强类型编译期检查避免运行时注册错误易维护全项目统一规则新人上手无成本三、常用实用用法除了标记接口Scrutor 还支持多种扫描规则适配不同项目场景。3.1 基于命名约定注册适合命名规范严格的现有项目按类名后缀批量注册services.Scan(scan scan .FromAssemblyOfProgram() // 自动注册所有以 Service 结尾的类 .AddClasses(classes classes.Where(type type.Name.EndsWith(Service))) .AsImplementedInterfaces() .WithScopedLifetime() // 自动注册所有以 Repository 结尾的仓储类 .AddClasses(classes classes.Where(type type.Name.EndsWith(Repository))) .AsImplementedInterfaces() .WithScopedLifetime() );3.2 基于命名空间注册适合按模块分层的项目按命名空间批量注册services.Scan(scan scan .FromAssemblyOfProgram() // 注册应用服务层 .AddClasses(classes classes.InNamespaces(MyProject.Application.Services)) .AsImplementedInterfaces() .WithScopedLifetime() // 注册数据仓储层 .AddClasses(classes classes.InNamespaces(MyProject.Infrastructure.Repositories)) .AsImplementedInterfaces() .WithScopedLifetime() );3.3 基于基类注册适合封装通用基类的项目批量注册继承基类的所有子类// 抽象基类封装通用日志、工具方法 public abstract class BaseService { protected readonly ILoggerBaseService _logger; protected BaseService(ILoggerBaseService logger) _logger logger; } // 自动扫描注册所有继承 BaseService 的类 services.Scan(scan scan .FromAssemblyOfBaseService() .AddClasses(classes classes.InheritedFromBaseService()) .AsSelf() .WithScopedLifetime() );3.4 注册为自身类型适合CQRS 命令/查询模式直接注册实现类本身services.Scan(scan scan .FromAssemblyOfProgram() .AddClasses(classes classes.AssignableToICommandHandler()) .AsSelf() // 不注册接口直接注册类本身 .WithTransientLifetime() ); // 使用时直接获取实现类 var handler _serviceProvider.GetRequiredServiceCreateUserCommandHandler();3.5 自定义注册策略控制重复服务的注册行为避免冲突services.Scan(scan scan .FromAssemblyOfProgram() .AddClasses(classes classes.AssignableToIValidator()) .UsingRegistrationStrategy(RegistrationStrategy.Skip) // 跳过已存在的注册 .AsImplementedInterfaces() .WithTransientLifetime() );可选策略Append追加注册默认Skip跳过已注册服务Replace替换已注册服务四、高级进阶用法4.1 装饰器模式Scrutor 原生支持装饰器轻松实现日志、缓存、事务等横切逻辑// 核心服务 装饰器都实现同一接口 public interface IOrderService { void CreateOrder(); } public class OrderService : IOrderService { } public class LoggingOrderDecorator : IOrderService { } // 先注册核心服务 services.Scan(scan scan.FromAssemblyOfIOrderService().AddClasses().AsImplementedInterfaces().WithScopedLifetime()); // 叠加装饰器执行顺序后注册的先执行 services.DecorateIOrderService, LoggingOrderDecorator();4.2 条件过滤注册通过特性标记跳过不需要自动注册的服务// 自定义跳过特性 [AttributeUsage(AttributeTargets.Class)] public class SkipAutoRegistrationAttribute : Attribute { } // 扫描时排除标记类 services.Scan(scan scan .FromApplicationDependencies() .AddClasses(classes classes .AssignableToIScopedDependency() .Where(t !t.HasAttributeSkipAutoRegistrationAttribute()) ) .AsImplementedInterfaces() .WithScopedLifetime() ); // 使用标记无需自动注册的类 [SkipAutoRegistration] public class TestService : IScopedDependency { }4.3 批量注册泛型服务适配仓储模式等泛型接口自动注册封闭泛型services.Scan(scan scan .FromAssemblyOfIRepository() .AddClasses(classes classes.AssignableTo(typeof(IRepository))) .AsClosedTypesOf(typeof(IRepository)) .WithScopedLifetime() ); // 直接使用 var userRepo _serviceProvider.GetRequiredServiceIRepositoryUser();五、各注册方式对比注册方式优点缺点推荐指数标记接口零配置、可读性高、编译期检查需要定义空接口⭐⭐⭐⭐⭐命名约定无需改代码、快速迁移强依赖命名规范⭐⭐⭐命名空间分层清晰、模块化管理需维护命名空间⭐⭐⭐⭐基类继承统一通用逻辑类耦合度偏高⭐⭐⭐注册自身适配 CQRS 模式面向实现编程灵活性低⭐⭐⭐六、落地最佳实践6.1 分层扫描注册按项目架构分层注册职责清晰便于排查问题// 应用层注册 public static IServiceCollection AddApplicationLayer(this IServiceCollection services) { services.Scan(scan scan .FromAssemblyOfApplicationLayerMarker() .AddClasses(classes classes.AssignableToIScopedDependency()) .AsImplementedInterfaces() .WithScopedLifetime() ); return services; } // 基础设施层注册 public static IServiceCollection AddInfrastructureLayer(this IServiceCollection services) { services.Scan(scan scan .FromAssemblyOfInfrastructureLayerMarker() .AddClasses(classes classes.AssignableToIRepository()) .AsImplementedInterfaces() .WithScopedLifetime() ); return services; }6.2 自动手动注册配合自动注册通用服务手动注册特殊配置服务// 批量自动注册 services.AddAutoDependencyInjection(); // 手动注册特殊服务带自定义配置 services.AddScopedIPaymentService(sp { var config sp.GetRequiredServiceIOptionsPaymentConfig(); return new AlipayPaymentService(config.Value.AppId); });6.3 测试环境适配单元测试中直接用 Mock 覆盖自动注册的服务// 测试项目重写注册 services.AddScopedIUserService, MockUserService(); services.AddScopedIOrderService, MockOrderService();七、总结Scrutor 让 .NET 依赖注入从手动编码变成自动匹配不同注册方式对应不同项目场景新项目搭建 →标记接口模式首选老项目迁移 →命名约定模式模块化架构 →命名空间模式CQRS 架构 →注册自身模式标记接口模式是最推荐的方案它兼顾简洁性、可读性和可维护性能显著提升开发效率降低团队协作成本是企业级 .NET 项目的标配实践。