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
相关产品推荐
相关产品推荐

