.NET Core仓储模式下解析两个DbContext及依赖注入错误解决
问题描述
拥有两个DbContext类型:
private static readonly AutomationContext _aContext; private static readonly RajaDb2Context _bContext;
同时定义了两个结构完全一致的仓储接口:
public interface IRepository<T> { T? GetById(object id); T? GetOne(Expression<Func<T, bool>> predicate); IEnumerable<T> Get(Expression<Func<T, bool>> predicate); IEnumerable<T> GetAll(); void Add(T entity); void AddRange(IEnumerable<T> entities); void Remove(T entity); void RemoveRange(IEnumerable<T> entities); void Update(T entity); void UpdateRange(IEnumerable<T> entities); } public interface IRepository2<T> { T? GetById(object id); T? GetOne(Expression<Func<T, bool>> predicate); IEnumerable<T> Get(Expression<Func<T, bool>> predicate); IEnumerable<T> GetAll(); void Add(T entity); void AddRange(IEnumerable<T> entities); void Remove(T entity); void RemoveRange(IEnumerable<T> entities); void Update(T entity); void UpdateRange(IEnumerable<T> entities); }
需求是将AutomationContext注入到IRepository<T>的实现,RajaDb2Context注入到IRepository2<T>的实现。编写了泛型解析器类:
public static class DependencyResolver<T> where T : class { private static readonly AutomationContext _aContext; private static readonly RajaDb2Context _bContext; public static IRepository<T> ResolveAutomation { get { return new Repository<T>(_aContext); } } public static IRepository2<T> Raja2 { get { return new RajaDb2Repository<T>(_bContext); } } }
但在DI容器注册时:
services.AddScoped(typeof(IRepository<>), DependencyResolver<>.ResolveAutomation); services.AddScoped(typeof(IRepository2<>), DependencyResolver<>.Raja2);
出现错误:
Error CS0305
Using the generic type 'DependencyResolver' requires 1 type arguments
仓储实现类Repository<T>如下:
public class Repository<T> : IRepository<T> where T : class { private readonly DbContext _context; public Repository(DbContext context) { _context = context; } public T? GetById(object id) { return _context.Set<T>().Find(id); } public T? GetOne(Expression<Func<T, bool>> predicate) { return _context.Set<T>().FirstOrDefault(predicate); } public IEnumerable<T> Get(Expression<Func<T, bool>> predicate) { return _context.Set<T>().Where(predicate).AsEnumerable(); } public IEnumerable<T> GetAll() { return _context.Set<T>().AsEnumerable(); } public void Add(T entity) { _context.Set<T>().Add(entity); _context.SaveChanges(); } public void AddRange(IEnumerable<T> entities) { _context.Set<T>().AddRange(entities); _context.SaveChanges(); } public void Remove(T entity) { _context.Set<T>().Remove(entity); _context.SaveChanges(); } public void RemoveRange(IEnumerable<T> entities) { _context.Set<T>().RemoveRange(entities); _context.SaveChanges(); } public void Update(T entity) { _context.Set<T>().Update(entity); _context.SaveChanges(); } public void UpdateRange(IEnumerable<T> entities) { _context.Set<T>().UpdateRange(entities); _context.SaveChanges(); } }
错误原因
- 泛型类访问错误:
DependencyResolver<T>是带类型参数的泛型类,无法通过DependencyResolver<>.ResolveAutomation这种开放式泛型语法访问其静态属性,必须指定具体的类型参数(如DependencyResolver<Entity>.ResolveAutomation),但DI注册泛型服务需要的是开放式泛型的映射,这种方式无法满足。 - 静态DbContext未初始化:
DependencyResolver<T>中的_aContext和_bContext是静态只读字段,但没有赋值,即使解决泛型问题,运行时也会抛出空引用异常,且静态字段无法利用DI容器的生命周期管理。
正确解决方案
步骤1:注册DbContext到DI容器
首先确保两个DbContext已正确注册到DI,示例如下(根据实际数据库类型调整):
// 注册AutomationContext services.AddDbContext<AutomationContext>(options => options.UseSqlServer("Your_Automation_Connection_String")); // 注册RajaDb2Context services.AddDbContext<RajaDb2Context>(options => options.UseSqlServer("Your_RajaDb2_Connection_String"));
步骤2:创建对应IRepository2的实现类
为了清晰绑定不同的DbContext,为IRepository2<T>创建专属实现类(复用Repository<T>的逻辑,仅依赖RajaDb2Context):
public class RajaDb2Repository<T> : IRepository2<T> where T : class { private readonly RajaDb2Context _context; public RajaDb2Repository(RajaDb2Context context) { _context = context; } public T? GetById(object id) { return _context.Set<T>().Find(id); } public T? GetOne(Expression<Func<T, bool>> predicate) { return _context.Set<T>().FirstOrDefault(predicate); } public IEnumerable<T> Get(Expression<Func<T, bool>> predicate) { return _context.Set<T>().Where(predicate).AsEnumerable(); } public IEnumerable<T> GetAll() { return _context.Set<T>().AsEnumerable(); } public void Add(T entity) { _context.Set<T>().Add(entity); _context.SaveChanges(); } public void AddRange(IEnumerable<T> entities) { _context.Set<T>().AddRange(entities); _context.SaveChanges(); } public void Remove(T entity) { _context.Set<T>().Remove(entity); _context.SaveChanges(); } public void RemoveRange(IEnumerable<T> entities) { _context.Set<T>().RemoveRange(entities); _context.SaveChanges(); } public void Update(T entity) { _context.Set<T>().Update(entity); _context.SaveChanges(); } public void UpdateRange(IEnumerable<T> entities) { _context.Set<T>().UpdateRange(entities); _context.SaveChanges(); } }
步骤3:注册泛型仓储接口与实现
直接通过DI的泛型注册能力,将接口与对应实现绑定,DI会自动注入匹配的DbContext:
// 绑定IRepository<T>到Repository<T>,自动注入AutomationContext services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); // 绑定IRepository2<T>到RajaDb2Repository<T>,自动注入RajaDb2Context services.AddScoped(typeof(IRepository2<>), typeof(RajaDb2Repository<>));
替代方案(不新增仓储实现类)
如果不想新增RajaDb2Repository<T>,可以通过工厂方法注册,手动指定注入的DbContext:
// 注册IRepository<T>,注入AutomationContext services.AddScoped(typeof(IRepository<>), sp => { var entityType = typeof(IRepository<>).GetGenericArguments()[0]; var repositoryType = typeof(Repository<>).MakeGenericType(entityType); var context = sp.GetRequiredService<AutomationContext>(); return (IRepository<object>)Activator.CreateInstance(repositoryType, context); }); // 注册IRepository2<T>,注入RajaDb2Context services.AddScoped(typeof(IRepository2<>), sp => { var entityType = typeof(IRepository2<>).GetGenericArguments()[0]; var repositoryType = typeof(Repository<>).MakeGenericType(entityType); var context = sp.GetRequiredService<RajaDb2Context>(); return (IRepository2<object>)Activator.CreateInstance(repositoryType, context); });
额外优化建议
- 两个仓储接口结构完全一致,可以考虑提取一个基础接口
IReadOnlyRepository<T>和IWriteRepository<T>,减少代码重复。 - 仓储实现中的
SaveChanges()建议放到业务层统一调用,避免每个操作都提交,提升事务控制灵活性。
内容的提问来源于stack exchange,提问作者Ehsan Akbar
相关产品推荐
相关产品推荐

