注入泛型仓储至BL服务时遇DbContext解析异常求助
原本将GenericRepository注入BL服务用于从Entity Framework获取数据时无异常,但添加另一个Scoped生命周期的BL服务后,抛出以下异常:
System.AggregateException HResult=0x80131500 Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: BL.Interfaces.IDoorsBL Lifetime: Scoped ImplementationType: BL.Services.DoorsBL': Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'DAL.Repositories.GenericRepository[DAL.Models.Door]'.)
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder) at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider() at Microsoft.Extensions.Hosting.HostBuilder.Build() at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() at Program.<Main>$(String[] args) in source\repos\IntimidatorServer\IntimidatorServer\Program.cs:line 48 This exception was originally thrown at this call stack: [External Code] Inner Exception 1: InvalidOperationException: Error while validating the service descriptor 'ServiceType: BL.Interfaces.IDoorsBL Lifetime: Scoped ImplementationType: BL.Services.DoorsBL': Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'DAL.Repositories.GenericRepository`1[DAL.Models.Door]'. Inner Exception 2: InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'DAL.Repositories.GenericRepository DAL.Models.Door
相关代码
GenericRepository代码
public class GenericRepository<T> : IGenericRepository<T> where T : class { private readonly DbContext _context; public GenericRepository(DbContext context) { _context = context; } public async Task<T> CreateAsync(T obj) { await _context.Set<T>().AddAsync(obj); return obj; } public async Task<bool> DeleteAsync(params object[] keys) { var entityToDelete = await _context.Set<T>().FindAsync(keys); if (entityToDelete != null) { _context.Set<T>().Remove(entityToDelete); return true; } else { return false; } } public async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> filter = null) { return filter == null ? await _context.Set<T>().ToListAsync() : (IEnumerable<T>)await _context.Set<T>().Where(filter).ToListAsync(); } public async Task<T> GetAsync(params object[] keys) { return await _context.Set<T>().FindAsync(keys); } public async Task SaveChangesAsync() { await _context.SaveChangesAsync(); } public Task<bool> UpdateAsync(T obj) { try { _context.Set<T>().Attach(obj); _context.Entry(obj).State = EntityState.Modified; return Task.FromResult(true); } catch (Exception ex) { return Task.FromResult(false); } } }
DoorsBL代码
public class DoorsBL : IDoorsBL { private IGenericRepository<Door> _repository; public DoorsBL(IGenericRepository<Door> repository) { _repository = repository; } public async Task<IEnumerable<Door>> GetAvailableDoors() { return await _repository.GetAllAsync(d => d.IsAvailble == true); } }
Program.cs代码
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("connectionString"))); builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>)); builder.Services.AddScoped<IDoorsBL, DoorsBL>();
DbContext代码
public class AppDbContext : DbContext { protected readonly IConfiguration Configuration; public AppDbContext(IConfiguration configuration) { Configuration = configuration; } public DbSet<Door> Doors { get; set; } public DbSet<Intimidator> Intimidators { get; set; } }
问题根源在于:GenericRepository<T>的构造函数依赖抽象类DbContext,但DI容器中仅注册了具体的AppDbContext类型,未将DbContext抽象映射到AppDbContext实现,导致容器无法找到DbContext的实例完成注入。
有两种解决方式:
方式一:修改GenericRepository依赖具体的AppDbContext
将GenericRepository<T>的构造函数参数改为AppDbContext:
private readonly AppDbContext _context; public GenericRepository(AppDbContext context) { _context = context; }
这种方式更直接,因为项目中实际使用的就是AppDbContext,无需依赖抽象的DbContext。
方式二:在DI容器中注册DbContext到AppDbContext的映射
在Program.cs中添加一行注册,让容器知道当需要DbContext时,提供AppDbContext的实例:
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("connectionString"))); // 添加这行映射 builder.Services.AddScoped<DbContext>(sp => sp.GetRequiredService<AppDbContext>()); builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>)); builder.Services.AddScoped<IDoorsBL, DoorsBL>();
两种方式任选其一即可解决依赖注入的解析问题。
内容的提问来源于stack exchange,提问作者Tal

