如何在通用仓储中实现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); } }
四、关键问题说明
- 原代码核心错误:
BaseRepository<T>中_dbContext.Set<T>()的T是领域实体,但DbContext中注册的是数据库实体(如UserDbTbl),导致无法正确访问数据库表,双泛型区分两类实体即可解决。 - 导航属性映射:只要配置了主实体的映射,且嵌套实体(如
Address)的映射已定义,AutoMapper会自动处理嵌套导航属性的转换。 - 增删改逻辑:新增/更新时需先将领域实体转为数据库实体,操作数据库后再将更新后的数据库实体转回领域实体,确保获取数据库生成的ID等字段。
内容的提问来源于stack exchange,提问作者mustafa00
相关产品推荐
相关产品推荐

