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

来源:这里教程网 时间:2026-02-21 17:42:06 作者:

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

Scrutor 是一个轻量级的第三方库,用来补足 .NET 原生

IServiceCollection
在批量服务注册上的短板。原生 API(比如
AddTransient
AddScoped
)只支持单类型注册;而 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()
只支持“单个开放泛型定义”,不能同时匹配
IValidator
IHandler
如果实现类本身是泛型(如
class GenericValidator<t> : IValidator<t></t></t>
),它仍会被注册,但实际解析时可能因构造函数参数缺失而失败——Scrutor 不校验可解析性,那是 DI 容器 runtime 的事

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

ValidateScopes = true
的集成测试。

相关推荐