C# Scrutor自动注册依赖方法 C#如何批量扫描和注册服务

2次阅读

scrutor 是补足 .net 原生 iservicecollection 批量注册短板的轻量级库,支持基于约定、泛型约束和程序集扫描的自动服务注册,避免手动反射导致的泛型闭包遗漏、生命周期不一致等问题。

C# Scrutor自动注册依赖方法 C#如何批量扫描和注册服务

Scrutor 是什么,为什么不用原生 IServiceCollection 扩展?

Scrutor 是一个轻量级的第三方库,用来补足 .NET 原生 IServiceCollection 在批量服务注册上的短板。原生 API(比如 AddTransientAddScoped)只支持单类型注册;而 Scrutor 提供了基于约定、泛型约束、程序集扫描等能力,让“自动发现并注册所有 IRepository<t></t> 实现类”这类需求变得可行。

常见错误是直接用反射遍历类型然后硬调 AddTransient——容易漏掉泛型闭包、忽略生命周期一致性、无法按接口继承链匹配。Scrutor 内部做了这些判断,且 API 更语义化。

使用前需安装:dotnet add package Scrutor

如何用 Scan() 批量注册同一程序集下的实现类?

这是最常用场景:把当前项目中所有实现了某个接口的类,按约定注册为对应生命周期服务。

services.Scan(scan => scan     .FromAssemblyOf<IRepository<int>>()         .Addclasses(classes => classes.AssignableTo<IRepository<Object>>())             .AsImplementedInterfaces()             .WithTransientLifetime() );

关键点说明:

  • FromAssemblyOf<...>()</...> 指定扫描范围,推荐用一个标志性接口(而非随便选个类),避免因程序集加载顺序出错
  • AssignableTo<irepository>>()</irepository> 匹配所有实现该接口(或其泛型变体)的类,包括 UserRepository : IRepository<user></user>
  • AsImplementedInterfaces() 自动将类注册为其所有公开实现的接口(不包括基类接口),比手写 As<irepository>>()</irepository> 更灵活
  • WithTransientLifetime() 统一设为 Transient;也可用 WithScopedLifetime()WithSingletonLifetime()

注意:如果某类实现了多个接口(如 IRepository<t></t>IQueryHandler<t></t>),它会被注册多次——Scrutor 默认行为如此,不是 bug

怎么排除测试类或内部类?

默认会扫到 internal 类甚至 [ExcludeFromCodeCoverage] 标记的类。必须显式过滤:

services.Scan(scan => scan     .FromAssemblyOf<IRepository<int>>()         .AddClasses(classes => classes             .AssignableTo<IRepository<object>>()             .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition))                 .AsImplementedInterfaces()                 .WithTransientLifetime() );

重点过滤条件:

  • !t.IsAbstract:跳过抽象类(否则注册失败)
  • !t.IsGenericTypeDefinition:跳过像 RepositoryBase<t></t> 这种未闭合的泛型定义(它们不能被实例化)
  • 若需进一步排除命名空间,可用 .Where(t => !t.Namespace.StartsWith("Tests."))

注册时如何绑定泛型接口与泛型实现?

比如你有 interface IValidator<t> { }</t>class UserValidator : IValidator<user> { }</user>,Scrutor 默认不会做泛型映射——它只认“完全匹配”的接口。

要支持泛型绑定,得用 AsClosedTypesOf()

services.Scan(scan => scan     .FromAssemblyOf<IValidator<int>>()         .AddClasses(classes => classes.AssignableTo<IValidator<object>>())             .AsClosedTypesOf(typeof(IValidator<>))             .WithTransientLifetime() );

这个调用会让 UserValidator 被注册为 IValidator<user></user>,而不是仅注册为 IValidator<object></object> 或丢弃。

⚠️ 容易踩的坑:

  • AsClosedTypesOf() 只支持“单个开放泛型定义”,不能同时匹配 IValidatorIHandler
  • 如果实现类本身是泛型(如 class GenericValidator<t> : IValidator<t></t></t>),它仍会被注册,但实际解析时可能因构造函数参数缺失而失败——Scrutor 不校验可解析性,那是 DI 容器 runtime 的事

复杂点在于泛型约束和构造函数依赖的隐式耦合,Scrutor 做的是类型注册,不是依赖图验证。上线前务必跑一遍 ValidateScopes = true 的集成测试。

text=ZqhQzanResources