不同Web项目注册自定义DbContext时如何复用现有通用CRUD类库?
解决方案
核心思路是将通用类库中硬编码的ApiContext改为泛型参数,通过泛型约束兼容所有继承自DbContext的自定义上下文,无需额外定义接口即可实现多项目适配。
步骤1:调整通用仓储定义
修改GenericRepository增加上下文泛型参数:
public class GenericRepository<TEntity, TContext> where TEntity : BaseModel where TContext : DbContext { public TContext Context { get; } internal DbSet<TEntity> DbSet; public GenericRepository(TContext context) { this.Context = context; this.DbSet = context.Set<TEntity>(); Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } // 原有方法无需修改,直接保留 public virtual async Task<TEntity> GetByIDAsync(object id) { return await DbSet.FindAsync(id); } public virtual void Insert(TEntity entity) { entity.CreatedDate = DateTime.UtcNow; DbSet.Add(entity); } // 其余原有方法保持不变 }
步骤2:调整工作单元定义
修改UnitOfWork增加上下文泛型参数:
public class UnitOfWork<TContext> : IUnitOfWork, IDisposable where TContext : DbContext { private readonly Dictionary<Type, object> repositories = new Dictionary<Type, object>(); private TContext _context; public UnitOfWork(TContext webApiContext) { _context = webApiContext; } public GenericRepository<TEntity, TContext> GetRepository<TEntity>() where TEntity : BaseModel { if (repositories.ContainsKey(typeof(TEntity))) return repositories[typeof(TEntity)] as GenericRepository<TEntity, TContext>; var repository = new GenericRepository<TEntity, TContext>(_context); repositories.Add(typeof(TEntity), repository); return repository; } public DbContext Context { get => _context; } public async Task<int> SaveAsync() { try { return await _context.SaveChangesAsync(); } catch (Exception) { // 原有异常处理逻辑保留 throw; } } public void Dispose() { _context?.Dispose(); } }
注意如果IUnitOfWork接口中定义了Context属性,需要将其返回值类型从ApiContext改为DbContext,保持兼容。
步骤3:封装通用注册扩展
在通用类库中新增服务注册扩展方法,简化新项目接入流程:
public static class CrudLibraryExtensions { public static IServiceCollection AddCommonCrud<TContext>(this IServiceCollection services, Action<DbContextOptionsBuilder> dbOptions) where TContext : DbContext { // 注册用户自定义的DbContext services.AddDbContext<TContext>(dbOptions); // 绑定对应上下文的工作单元 services.AddTransient<IUnitOfWork, UnitOfWork<TContext>>(); // 注册通用基础服务 services.AddTransient<IDataMapper, DataMapper>(); services.AddTransient(typeof(ICrudService<>), typeof(CrudService<>)); return services; } }
步骤4:新项目接入流程
- 新项目自定义的
DbContext直接继承官方DbContext即可,无需额外实现:
// 会计项目自定义上下文示例 public class AccountingDbContext : DbContext { public DbSet<Account> Accounts { get; set; } // 其他项目专属DbSet public AccountingDbContext(DbContextOptions<AccountingDbContext> options) : base(options){} protected override void OnModelCreating(ModelBuilder modelBuilder) { // 项目专属实体配置 modelBuilder.Entity<Account>().ApplyBaseModelSchema(); } }
- 新项目启动类直接调用扩展方法注册即可:
public void ConfigureServices(IServiceCollection services) { services.AddCommonCrud<AccountingDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("项目数据库连接字符串Key"))); // 其余项目专属服务注册逻辑 }
可选方案:基于IDbContext接口实现
如果需要对上下文做更多自定义约束,也可以通过接口实现:
- 定义
IDbContext接口,包含通用类库用到的上下文方法:
public interface IDbContext { DbSet<T> Set<T>() where T : class; Task<int> SaveChangesAsync(CancellationToken cancellationToken = default); // 其他需要用到的方法/属性 }
- 所有自定义DbContext实现该接口,
GenericRepository和UnitOfWork注入IDbContext即可,注册时指定对应实现类即可。
内容的提问来源于stack exchange,提问作者Lawrence
相关产品推荐
相关产品推荐

