如何在GenericRepository中实现GenericDbContext?解决多DbContext实例化问题
多DbContext下通用GenericRepository的正确实现方案
现有代码核心问题
GenericDbContextFactory始终创建基础DbContext实例,无法生成你指定的TContext类型,导致as TContext转换失败,最终_context为null- 未对转换后的context做null校验,直接调用
Set<TTable>()会触发空引用异常 - 工厂类缺乏创建特定
TContext的能力
1. 重构DbContext工厂,支持创建指定类型的TContext
修改工厂接口与实现,使其能通过反射创建指定的TContext实例(前提是所有自定义DbContext都实现了接受DbContextOptions<TContext>的构造函数,这是EF Core的标准要求):
public interface IGenericDbContextFactory { TContext Create<TContext>(string connectionString) where TContext : DbContext; } public class GenericDbContextFactory : IGenericDbContextFactory { public TContext Create<TContext>(string connectionString) where TContext : DbContext { if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); // 构建对应TContext的配置选项 var optionsBuilder = new DbContextOptionsBuilder<TContext>(); optionsBuilder.UseSqlServer(connectionString); // 通过反射获取并调用TContext的构造函数 var constructor = typeof(TContext).GetConstructor(new[] { typeof(DbContextOptions<TContext>) }); if (constructor == null) throw new InvalidOperationException($"类型 {typeof(TContext).Name} 未实现接受 DbContextOptions<{typeof(TContext).Name}> 的构造函数"); return (TContext)constructor.Invoke(new[] { optionsBuilder.Options }); } }
2. 修正GenericRepository,确保Context非空且类型正确
调整Repository中的方法逻辑,使用泛型工厂创建正确的TContext,并添加严格的null校验:
public class GenericRepository<TTable, TContext> : IGenericRepository<TTable, TContext> where TTable : class where TContext : DbContext { private TContext _context; private IGenericDbContextFactory _contextFactory; private DbSet<TTable> _table; private readonly string _connectionString; public GenericRepository(IGenericDbContextFactory contextFactory, string connectionString) { _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); _context = GetNew(); } public virtual void Reset() { _context.Dispose(); _context = GetNew(); } public TContext GetNew() { // 创建指定类型的TContext实例 var context = _contextFactory.Create<TContext>(_connectionString); // 严格校验,避免空引用 if (context == null) throw new InvalidOperationException($"无法创建 {typeof(TContext).Name} 实例"); _table = context.Set<TTable>(); return context; } public async Task Save() { try { await _context.SaveChangesAsync(); } catch (Exception ex) { Reset(); // 保留原始异常堆栈,便于排查问题 throw new Exception("保存数据失败", ex); } _context.ChangeTracker.Clear(); } // 实现IGenericRepository接口的其他方法... }
3. 关键注意事项
- 所有自定义DbContext必须实现标准构造函数,示例:
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } // 定义DbSet... } - 依赖注入时,需将
IGenericDbContextFactory注册为GenericDbContextFactory的实例 - 借助
Reset方法及时释放旧Context,避免资源泄漏
内容的提问来源于stack exchange,提问作者user1531040
相关产品推荐
相关产品推荐

