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

.NET Core依赖注入下如何复用CRUD通用代码?

.NET Core 中CRUD仓储的代码复用方案

核心思路

通过泛型基类仓储封装通用CRUD逻辑,将重复的Add/Update/Delete代码抽象到泛型类中,仅针对不同实体的特殊逻辑在子类中扩展,彻底消除代码冗余。

步骤1:定义实体通用接口

为所有实体统一主键的访问规范,方便泛型基类处理主键操作:

public interface IEntity<TId>
{
    TId Id { get; set; }
}

修改基类ChangeTracker实现该接口(确保所有实体继承它后自动拥有主键属性):

public abstract class ChangeTracker : IEntity<int>
{
    public int Id { get; set; }
    public string ModifiedBy { get; set; }
    public byte StatusId { get; set; }
}

步骤2:实现泛型基类仓储

创建泛型类CrudRepositoryBase<TModel, TId>,实现ICrudRepository<TModel, TId>接口,封装通用CRUD逻辑:

public class CrudRepositoryBase<TModel, TId> : BaseDao, ICrudRepository<TModel, TId>
    where TModel : class, IEntity<TId>, ChangeTracker, new()
{
    public CrudRepositoryBase(CustomDbContext dbContext) : base(dbContext)
    {
    }

    public async Task<TModel> AddAsync(TModel model, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(model);

        var entity = (await CustomDbContext
            .Set<TModel>()
            .AddAsync(model, cancellationToken)
            .ConfigureAwait(false)).Entity;

        await SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return entity;
    }

    public async Task DeleteAsync(TId id, string userName, CancellationToken cancellationToken)
    {
        var entity = new TModel { Id = id };
        CustomDbContext.Set<TModel>().Attach(entity);
        
        entity.ModifiedBy = userName;
        entity.StatusId = (byte)Status.Inactive;

        await SaveChangesAsync(cancellationToken).ConfigureAwait(false);
    }

    public async Task<TModel> UpdateAsync(TModel model, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(model);

        CustomDbContext.Set<TModel>().Attach(model);
        await SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return model;
    }
}

步骤3:简化具体实体仓储

现在PersonCrudRepository无需重复编写CRUD方法,直接继承泛型基类即可,仅需处理实体专属逻辑:

public class PersonCrudRepository : CrudRepositoryBase<Person, int>, IPersonCrudRepository
{
    public PersonCrudRepository(CustomDbContext dbContext) : base(dbContext)
    {
    }

    // 若Person有特殊业务逻辑(如自定义查询),在此添加即可
}

步骤4:依赖注入注册

直接注册具体仓储,或通过泛型批量注册简化多实体场景:

public virtual void ConfigureServices(IServiceCollection services)
{
    // 注册单个实体仓储
    services.AddScoped<IPersonCrudRepository, PersonCrudRepository>();

    // 多实体场景下,可批量注册泛型仓储
    // services.AddScoped(typeof(ICrudRepository<,>), typeof(CrudRepositoryBase<,>));
}

额外优化建议

  • 若实体主键类型不统一,可通过IEntity<TId>的泛型参数灵活适配,比如IEntity<Guid>。
  • 若部分实体需要不同的删除逻辑(如物理删除),可在泛型基类中将DeleteAsync定义为虚方法,子类重写即可。
  • 可将SaveChangesAsync的调用逻辑抽离,让上层服务决定提交时机,提升事务控制的灵活性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:25:32