You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core WebAPI使用Scrutor扫描注册服务时依赖注入报错

问题分析与解决:ASP.NET Core中Scrutor扫描服务注册失败问题

问题描述

我正在构建一个ASP.NET Core WebAPI应用,手动注册服务的配置运行完全正常:

public void ConfigureServices(IServiceCollection services)
{
    var settings = Configuration.Get<Settings>();
    CosmosClient client = new CosmosClient(settings.CosmosDB.EndpointUrl, settings.CosmosDB.PrimaryKey);
    CosmosDbContainerFactory cosmosDbClientFactory = new CosmosDbContainerFactory(client, settings.CosmosDB.DatabaseName, settings.CosmosDB.Containers);

    services.AddSingleton<ICosmosDbContainerFactory>(cosmosDbClientFactory);
    services.AddTransient<IFamilyService, FamilyService>();
    services.AddTransient<IFamilyRepository, FamilyRepository>();

    services.AddControllers();
}

但尝试用Scrutor扫描替换手动服务注册后,配置如下:

public void ConfigureServices(IServiceCollection services)
{
    var settings = Configuration.Get<Settings>();
    CosmosClient client = new CosmosClient(settings.CosmosDB.EndpointUrl, settings.CosmosDB.PrimaryKey);
    CosmosDbContainerFactory cosmosDbClientFactory = new CosmosDbContainerFactory(client, settings.CosmosDB.DatabaseName, settings.CosmosDB.Containers);

    services.AddSingleton<ICosmosDbContainerFactory>(cosmosDbClientFactory);

    services.Scan(s => s
        .FromAssembliesOf(typeof(IApiAssemblyMarker))
        .AddClasses(false)
        .UsingRegistrationStrategy(RegistrationStrategy.Append)
        .AsImplementedInterfaces()
        .WithTransientLifetime()
        ); 

    services.AddControllers();
}

出现以下错误:

Error while validating the service descriptor 'ServiceType: FBAuthDemoAPI.Services.Contract.IFamilyService Lifetime: Transient ImplementationType: FBAuthDemoAPI.Services.Implementation.FamilyService': Unable to resolve service for type 'Microsoft.Azure.Cosmos.CosmosClient' while attempting to activate 'FBAuthDemoAPI.CosmosDBFactory.CosmosDbContainerFactory'.

相关类定义:

public interface IFamilyService
{   
}

public class FamilyService : IFamilyService
{
    private readonly IFamilyRepository _familyRepository;

    public FamilyService(IFamilyRepository familyRepository)
    {
        this._familyRepository = familyRepository;
    }
}   

public interface IGenericRepository<T> where T : class
{
}   

public abstract class GenericRepository<T> : IGenericRepository<T> where T : Entity
{
    private readonly Container _container;
    private readonly ICosmosDbContainerFactory _cosmosDbContainerFactory;

    public abstract string DatabaseName { get; }
    public abstract string ContainerName { get; }

    protected GenericRepository(ICosmosDbContainerFactory cosmosDbContainerFactory)
    {
        this._cosmosDbContainerFactory = cosmosDbContainerFactory ?? throw new ArgumentNullException(nameof(ICosmosDbContainerFactory));
        this._container = this._cosmosDbContainerFactory.GetContainer(ContainerName)._container;
    }
}   

public interface IFamilyRepository : IGenericRepository<Family>
{
}

public class FamilyRepository : GenericRepository<Family>, IFamilyRepository
{       
    public override string DatabaseName => "FamilyDB";
    public override string ContainerName => "Family";

    public FamilyRepository(ICosmosDbContainerFactory factory) :
        base(factory)
    {
    }   
}       

错误原因

问题出在Scrutor的扫描规则上:

  • 你使用了AddClasses(false),这个配置会扫描程序集中所有非抽象类,包括CosmosDbContainerFactory。
  • Scrutor会自动为CosmosDbContainerFactory注册它实现的所有接口,同时也会注册它自身作为服务,并且默认是Transient生命周期。
  • 虽然你手动注册了ICosmosDbContainerFactory的单实例,但DI容器在解析依赖时,可能会尝试创建CosmosDbContainerFactory的新实例(因为Scrutor注册了该类自身的服务),而CosmosDbContainerFactory的构造函数依赖CosmosClient,但你并没有把CosmosClient注册到DI容器中,所以出现了无法解析CosmosClient的错误。

修复方案

有两种可行的修复方式:

方式一:排除CosmosDbContainerFactory不让Scrutor扫描

修改Scrutor的扫描规则,过滤掉CosmosDbContainerFactory,避免自动注册它:

services.Scan(s => s
    .FromAssembliesOf(typeof(IApiAssemblyMarker))
    .AddClasses(classes => classes.Where(type => type != typeof(CosmosDbContainerFactory)))
    .UsingRegistrationStrategy(RegistrationStrategy.Append)
    .AsImplementedInterfaces()
    .WithTransientLifetime()
);

方式二:注册CosmosClient并让Scrutor正确处理CosmosDbContainerFactory

把CosmosClient也注册到DI容器,同时让Scrutor为CosmosDbContainerFactory注册单实例服务,替代手动注册:

public void ConfigureServices(IServiceCollection services)
{
    var settings = Configuration.Get<Settings>();
    // 注册CosmosClient为单实例
    services.AddSingleton(new CosmosClient(settings.CosmosDB.EndpointUrl, settings.CosmosDB.PrimaryKey));
    
    services.Scan(s => s
        .FromAssembliesOf(typeof(IApiAssemblyMarker))
        // 单独处理CosmosDbContainerFactory,注册为单实例
        .AddClasses(classes => classes.AssignableTo<ICosmosDbContainerFactory>())
        .AsSelfWithInterfaces()
        .WithSingletonLifetime()
        // 处理其他服务
        .AddClasses(classes => classes.Where(type => !type.IsAssignableTo<ICosmosDbContainerFactory>()))
        .AsImplementedInterfaces()
        .WithTransientLifetime()
    ); 

    services.AddControllers();
}

关于更新后配置的疑问解答

你更新后的分类型注册能正常运行,不是Scrutor的限制,而是因为之前的扫描规则太宽泛:

  • 之前的AddClasses(false)会把所有非抽象类都纳入扫描,包括CosmosDbContainerFactory,导致Scrutor自动注册了它的Transient实例,和你手动注册的ICosmosDbContainerFactory单实例产生冲突。
  • 更新后的代码明确指定了不同类型的扫描规则:
    • 对ICosmosDbContainerFactory实现类注册为单实例
    • 对仓储类和服务类分别注册对应的接口和生命周期
      这种精准的过滤避免了自动注册不需要的服务,自然就解决了依赖解析的问题。

内容的提问来源于stack exchange,提问作者One Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:42:28