You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不同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:新项目接入流程

  1. 新项目自定义的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();
    }
}
  1. 新项目启动类直接调用扩展方法注册即可:
public void ConfigureServices(IServiceCollection services)
{
    services.AddCommonCrud<AccountingDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("项目数据库连接字符串Key")));
    
    // 其余项目专属服务注册逻辑
}

可选方案:基于IDbContext接口实现

如果需要对上下文做更多自定义约束,也可以通过接口实现:

  1. 定义IDbContext接口,包含通用类库用到的上下文方法:
public interface IDbContext
{
    DbSet<T> Set<T>() where T : class;
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    // 其他需要用到的方法/属性
}
  1. 所有自定义DbContext实现该接口,GenericRepository和UnitOfWork注入IDbContext即可,注册时指定对应实现类即可。

内容的提问来源于stack exchange,提问作者Lawrence

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 17:45:02