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

如何在通用仓储中实现Db类与Entity类的AutoMapper映射?

通用仓储与AutoMapper映射解决方案

一、修正泛型仓储的核心设计

当前BaseRepository<T>的泛型参数仅指向领域实体,但数据库操作需要对应的数据库表实体,因此需要调整为双泛型,明确区分两类实体:

// 调整通用接口,新增数据库实体约束
public interface IAsyncRepository<T, TDb> 
    where T : class
    where TDb : class
{
    Task<T> GetByIdAsync(int id);
    Task<IReadOnlyList<T>> ListAllAsync();
    Task<T> AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
}

// 实现双泛型仓储
public class BaseRepository<T, TDb> : IAsyncRepository<T, TDb> 
    where T : class
    where TDb : class
{
    protected readonly RegionManagementDbContext _dbContext;
    private readonly IMapper _mapper;

    public BaseRepository(RegionManagementDbContext dbContext, IMapper mapper)
    {
        _dbContext = dbContext;
        _mapper = mapper;
    }

    public async Task<IReadOnlyList<T>> ListAllAsync()
    {
        var dbDataList = await _dbContext.Set<TDb>().ToListAsync();
        return _mapper.Map<IReadOnlyList<T>>(dbDataList);
    }

    public async Task<T> AddAsync(T entity)
    {
        var dbEntity = _mapper.Map<TDb>(entity);
        await _dbContext.Set<TDb>().AddAsync(dbEntity);
        await _dbContext.SaveChangesAsync();
        return _mapper.Map<T>(dbEntity);
    }

    public virtual async Task<T> GetByIdAsync(int id)
    {
        var dbEntity = await _dbContext.Set<TDb>().FindAsync(id);
        return _mapper.Map<T>(dbEntity);
    }

    public async Task DeleteAsync(T entity)
    {
        var dbEntity = _mapper.Map<TDb>(entity);
        _dbContext.Set<TDb>().Remove(dbEntity);
        await _dbContext.SaveChangesAsync();
    }

    public async Task UpdateAsync(T entity)
    {
        var dbEntity = _mapper.Map<TDb>(entity);
        _dbContext.Set<TDb>().Update(dbEntity);
        await _dbContext.SaveChangesAsync();
    }
}

二、配置AutoMapper映射Profile

针对每组数据库实体与领域实体创建映射,导航属性会自动匹配同名字段(需确保嵌套实体的映射已定义):

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        // 数据库实体到领域实体的映射,忽略领域实体不存在的字段
        CreateMap<UserDbTbl, User>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.UserName))
            // 导航属性自动映射,无需额外配置(只要Address的映射已定义)
            .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address));

        // 反向映射,用于新增、更新时将领域实体转成数据库实体
        CreateMap<User, UserDbTbl>();

        // 其他实体同理扩展,比如ProductDbTbl与Product
        // CreateMap<ProductDbTbl, Product>().ReverseMap();
    }
}

三、专用仓储的扩展(可选)

如果需要针对特定实体添加自定义逻辑,可继承通用仓储:

public interface IUserRepository : IAsyncRepository<User, UserDbTbl>
{
    Task<User> GetByUserNameAsync(string userName);
}

public class UserRepository : BaseRepository<User, UserDbTbl>, IUserRepository
{
    public UserRepository(RegionManagementDbContext dbContext, IMapper mapper) 
        : base(dbContext, mapper)
    {
    }

    public async Task<User> GetByUserNameAsync(string userName)
    {
        var dbEntity = await _dbContext.Set<UserDbTbl>()
            .FirstOrDefaultAsync(u => u.UserName == userName);
        return _mapper.Map<User>(dbEntity);
    }
}

四、关键问题说明

  1. 原代码核心错误:BaseRepository<T>中_dbContext.Set<T>()的T是领域实体,但DbContext中注册的是数据库实体(如UserDbTbl),导致无法正确访问数据库表,双泛型区分两类实体即可解决。
  2. 导航属性映射:只要配置了主实体的映射,且嵌套实体(如Address)的映射已定义,AutoMapper会自动处理嵌套导航属性的转换。
  3. 增删改逻辑:新增/更新时需先将领域实体转为数据库实体,操作数据库后再将更新后的数据库实体转回领域实体,确保获取数据库生成的ID等字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 09:55:15