.NET Core下使用AutoMapper配合泛型仓储提示无ProjectTo定义如何解决
问题原因
ProjectTo是AutoMapper专为IQueryable类型设计的扩展方法,作用是在数据库查询阶段就完成实体到DTO的投影,生成仅查询所需字段的SQL,避免全字段查询。你当前的写法存在两个错误:- 调用顺序错误:
GetAllAsync()返回的是Task<IEnumerable<Category>>,你未先await拿到返回结果就直接调用ProjectTo,自然找不到对应方法 - 类型不匹配:就算你先await拿到结果,得到的是已经执行完查询的内存集合
IEnumerable<Category>,不是IQueryable,无法适配ProjectTo的入参要求
- 调用顺序错误:
- 你当前的泛型仓储
GetAllAsync方法直接调用了ToListAsync,已经触发了数据库查询,后续就算拿到IQueryable也无法再做查询层面的投影优化。
解决方案
方案1:修改泛型仓储,保留ProjectTo的SQL投影优化(推荐)
该方案可以让AutoMapper参与到SQL生成中,仅查询DTO需要的字段,性能更好。
首先修改泛型仓储,新增返回IQueryable<TEntity>的方法:
// 泛型仓储中新增方法 public IQueryable<TEntity> GetAll() { return _dbSet.AsQueryable(); }
然后调整控制器调用逻辑,注意ProjectTo要放在await之前,作用在IQueryable上:
public async Task<IActionResult> GetCategories() { var c = await _unitOfWork.CategoryRepositoryAsync.GetAll() .ProjectTo<CategoryResponse>(_mapper.ConfigurationProvider) .ToListAsync(); return Ok(c); }
注意:
ProjectTo的泛型参数直接填DTO类型CategoryResponse即可,不需要加IEnumerable<>,AutoMapper会自动处理集合映射。
方案2:不修改仓储,使用IMapper.Map做内存映射(适合不想改仓储结构的场景)
如果不想调整现有仓储的代码,直接在拿到查询结果后用普通的Map方法做内存映射即可:
public async Task<IActionResult> GetCategories() { var categoryList = await _unitOfWork.CategoryRepositoryAsync.GetAllAsync(); var c = _mapper.Map<IEnumerable<CategoryResponse>>(categoryList); return Ok(c); }
该方案的缺点是会先查询Category的所有字段到内存再做映射,性能略低于方案1,适合数据量小的场景。
内容的提问来源于stack exchange,提问作者Eduard
相关产品推荐
相关产品推荐

