ABP框架扩展BasicRepositoryBase添通用仓储方法遇依赖注入错误
问题:ABP框架扩展仓储后依赖注入失败
我在ABP框架中扩展BasicRepositoryBase,为所有自定义仓储添加通用CreateOrUpdate方法,完成以下操作后,在数据种子类中依赖注入IFMSRepository<FairArea, Guid>时触发Autofac依赖解析异常,提示无法解析该参数、无法构造目标类:
- 定义泛型接口
IFMSRepository<TEntity, TKey>,继承IBasicRepository和IRepository,新增CreateOrUpdate方法 - 实现抽象基类
FMSRepository<TEntity,TKey>,继承BasicRepositoryBase并实现IFMSRepository接口,完成CreateOrUpdate逻辑,同时声明多个抽象方法 - 在
ApplicationModule中尝试两种服务注册方式:直接注册泛型接口与实现类、反射扫描注册
相关代码
1. IFMSRepository接口代码
using System; using System.Threading.Tasks; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; namespace FMS.Domain.Repositories { public interface IFMSRepository<TEntity, TKey> : IBasicRepository<TEntity,TKey>, IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> { Task<TEntity> CreateOrUpdate(TEntity entity); } }
2. FMSRepository抽象基类代码
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Identity; namespace FMS.Domain.Repositories { public abstract class FMSRepository<TEntity, TKey> : BasicRepositoryBase<TEntity, TKey>, IFMSRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> { public async Task<TEntity> CreateOrUpdate(TEntity entity ) { TEntity resultEntity; if (entity.Id == null) { resultEntity = await InsertAsync(entity); } else { var checkEntity = await GetAsync(entity.Id); if (checkEntity != null) resultEntity = await UpdateAsync(entity); else resultEntity = await InsertAsync(entity); } return resultEntity; } public abstract Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default); public abstract Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default); public abstract Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default); public abstract Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default); public abstract Task<IQueryable<TEntity>> GetQueryableAsync(); public abstract IQueryable<TEntity> WithDetails(); public abstract IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors); public abstract Task<IQueryable<TEntity>> WithDetailsAsync(); public abstract Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors); } }
3. 服务注册方式1:直接注册泛型
public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpAutoMapperOptions>(options => { options.AddMaps<FMSApplicationModule>(); }); context.Services.AddScoped(typeof(IFMSRepository<,>), typeof(FMSRepository<,>)); }
4. 服务注册方式2:反射扫描注册
public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpAutoMapperOptions>(options => { options.AddMaps<FMSApplicationModule>(); }); System.Reflection.Assembly.GetExecutingAssembly() .GetTypes() .Where(item => item.GetInterfaces() .Where(i => i.IsGenericType).Any(i => i.GetGenericTypeDefinition() == typeof(IFMSRepository<,>)) && !item.IsAbstract && !item.IsInterface) .ToList() .ForEach(assignedTypes => { var serviceType = assignedTypes.GetInterfaces().First(i => i.GetGenericTypeDefinition() == typeof(IFMSRepository<,>)); context.Services.AddScoped(serviceType, assignedTypes); }); }
5. 数据种子类代码
using System; using System.Threading.Tasks; using FMS.Domain.Repositories; using FMS.Fairs; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; namespace FMS.DataSeed { public class FMSDataSeederContributer : IDataSeedContributor, ITransientDependency { private readonly IFMSRepository<FairArea, Guid> _fairAreaRepository; private readonly ICurrentTenant _currentTenant; public FMSDataSeederContributer( IFMSRepository<FairArea,Guid> fairAreaRepository, ICurrentTenant currentTenant) { _fairAreaRepository = fairAreaRepository; _currentTenant = currentTenant; } public async Task SeedAsync(DataSeedContext context) { using (_currentTenant.Change(context?.TenantId)) { if (context?.TenantId == null) { return; } if (await _fairAreaRepository.GetCountAsync() > 0) { return; } await _fairAreaRepository.CreateOrUpdate( new FairArea { Name = "Name of Area", Type = FairAreaType.ClosedArea, Description = "Description of Area", Address = "Country of Area", TenantId = _currentTenant.Id } ); } } } }
错误日志
[23:12:10 INF] Started database migrations...[23:12:10 INF] Migrating schema for host database...[23:12:11 INF] Executing host database seed...Unhandled exception. Autofac.Core.DependencyResolutionException: An exception was thrown while activating FMS.DataSeed.FMSDataSeederContributer. ---> Autofac.Core.DependencyResolutionException: None of the constructors found on type 'FMS.DataSeed.FMSDataSeederContributer' can be invoked with the available services and parameters:Cannot resolve parameter 'FMS.Domain.Repositories.IFMSRepository`2[FMS.Fairs.FairArea,System.Guid] fairAreaRepository' of constructor 'Void .ctor(FMS.Domain.Repositories.IFMSRepository`2[FMS.Fairs.FairArea,System.Guid], Volo.Abp.MultiTenancy.ICurrentTenant)'. See https://autofac.rtfd.io/help/no-constructors-bindable for more info. at Autofac.Core.Activators.Reflection.ReflectionActivator.<>c__DisplayClass14_0.<UseSingleConstructorActivation>b__0(ResolveRequestContext ctxt, Action`1 next) at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Builder.RegistrationBuilder`3.<>c__DisplayClass41_0.<PropertiesAutowired>b__0(ResolveRequestContext ctxt, Action`1 next) at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next) --- End of inner exception stack trace --- at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt) at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request) at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest request) at Autofac.Core.Resolving.ResolveOperation.Execute(ResolveRequest request) at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest request) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType) at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Volo.Abp.Data.DataSeeder.SeedAsync(DataSeedContext context) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at FMS.Data.FMSDbMigrationService.SeedDataAsync(Tenant tenant) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.Domain/Data/FMSDbMigrationService.cs:line 103 at FMS.Data.FMSDbMigrationService.MigrateAsync() in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.Domain/Data/FMSDbMigrationService.cs:line 53 at FMS.DbMigrator.DbMigratorHostedService.StartAsync(CancellationToken cancellationToken) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/DbMigratorHostedService.cs:line 36 at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at FMS.DbMigrator.Program.Main(String[] args) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/Program.cs:line 30 at FMS.DbMigrator.Program.<Main>(String[] args)bash: line 1: 28828 Abort trap: 6 "/usr/local/share/dotnet/dotnet" "/Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/bin/Debug/net7.0/FMS.DbMigrator.dll" Press any key to continue...^ASaving session......copying shared history......saving history...truncating history files......completed. [Process completed]
问题原因与解决方案
核心原因
FMSRepository<TEntity,TKey>是抽象类,Autofac无法实例化抽象类,直接注册泛型接口到抽象类的方式无效- 反射扫描注册代码只查找非抽象的仓储实现类,但你没有为
FairArea编写具体的仓储实现类,因此没有对应的服务注册项
解决步骤
1. 为具体实体编写仓储实现类
以FairArea为例,创建继承FMSRepository<FairArea, Guid>的具体仓储类,实现所有抽象方法(若使用EF Core,建议继承EfCoreRepositoryBase而非直接继承BasicRepositoryBase):
using FMS.Fairs; using System; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; namespace FMS.Domain.Repositories { public class FairAreaRepository : FMSRepository<FairArea, Guid>, IFMSRepository<FairArea, Guid> { public FairAreaRepository(IDbContextProvider<FMSDbContext> dbContextProvider) : base(dbContextProvider) { } // 直接调用基类方法实现所有抽象接口 public override Task DeleteAsync(Expression<Func<FairArea, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default) { return base.DeleteAsync(predicate, autoSave, cancellationToken); } public override Task DeleteDirectAsync(Expression<Func<FairArea, bool>> predicate, CancellationToken cancellationToken = default) { return base.DeleteDirectAsync(predicate, cancellationToken); } public override Task<FairArea> FindAsync(Expression<Func<FairArea, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default) { return base.FindAsync(predicate, includeDetails, cancellationToken); } public override Task<FairArea> GetAsync(Expression<Func<FairArea, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default) { return base.GetAsync(predicate, includeDetails, cancellationToken); } public override Task<IQueryable<FairArea>> GetQueryableAsync() { return base.GetQueryableAsync(); } public override IQueryable<FairArea> WithDetails() { return base.WithDetails(); } public override IQueryable<FairArea> WithDetails(params Expression<Func<FairArea, object>>[] propertySelectors) { return base.WithDetails(propertySelectors); } public override Task<IQueryable<FairArea>> WithDetailsAsync() { return base.WithDetailsAsync(); } public override Task<IQueryable<FairArea>> WithDetailsAsync(params Expression<Func<FairArea, object>>[] propertySelectors) { return base.WithDetailsAsync(propertySelectors); } } }
2. 调整服务注册
两种可选方式:
- 手动注册具体仓储:在
ApplicationModule的ConfigureServices中添加context.Services.AddScoped<IFMSRepository<FairArea, Guid>, FairAreaRepository>(); - 自动扫描注册:保留原反射扫描代码,现在会自动找到非抽象的
FairAreaRepository并完成注册,无需手动逐个添加
3. 优化CreateOrUpdate方法
原方法中entity.Id == null的判断仅适用于可空主键,对于Guid这类非空值类型主键,新实体Id通常为默认值(Guid.Empty),建议改用ABP内置方法判断实体状态:
public async Task<TEntity> CreateOrUpdate(TEntity entity ) { if (EntityHelper.IsTransient(entity)) // 兼容所有主键
相关产品推荐
相关产品推荐

