Web API结合Sieve、DTO与AutoMapper实现数据库侧过滤排序
问题描述
在Web API项目中使用Sieve组件做数据过滤、排序、分页时,项目采用DTO作为接口返回结构,使用AutoMapper做数据库实体到DTO的映射,最初编写的接口代码如下:
[HttpGet] public async Task<ActionResult<IEnumerable<RdtoSetup>>> GetSetups([FromQuery] SieveModel sieveModel) { if (_context.Setups == null) { return NotFound(); } var setups = _context.Setups.AsNoTracking(); var results = _mapper.Map<IEnumerable<RdtoSetup>>(await setups.ToListAsync()); var filtered = _sieveProcessor.Apply(sieveModel, results); return Ok(filtered); }
原有代码的核心问题
这种写法存在明显的性能缺陷:
- 代码中提前调用
await setups.ToListAsync()会把Setups表的全量数据全部加载到应用内存中,完全无法利用数据库的索引、查询优化能力 - 全量数据加载完成后才做对象映射、Sieve过滤排序,当表数据量较大时,内存占用、查询耗时都会非常高,完全无法发挥Sieve组件的性能优势
优化方案
不需要调整现有AutoMapper的映射配置,只需要调整查询执行顺序,改用AutoMapper的投影查询能力即可,修正后的代码如下:
[HttpGet] public async Task<ActionResult<IEnumerable<RdtoSetup>>> GetSetups([FromQuery] SieveModel sieveModel) { if (_context.Setups == null) { return NotFound(); } var setups = _context.Setups.AsQueryable(); var results = _mapper.ProjectTo<RdtoSetup>(setups); var filtered = _sieveProcessor.Apply(sieveModel, results); return Ok(await filtered.ToListAsync()); }
优化原理
- 全程保留
IQueryable类型,不会提前触发数据库查询,EF Core会持续跟踪整个查询逻辑,最终统一翻译成SQL语句执行 - 用
ProjectTo<RdtoSetup>()替换原来的Map()方法:AutoMapper会根据实体和DTO的映射配置,自动生成SQL查询需要的SELECT字段投影,不需要手动编写Select()语句,同时不会加载DTO不需要的表字段 - Sieve的过滤、排序、分页逻辑会在数据库查询执行前应用,所有筛选逻辑都会翻译成SQL的WHERE、ORDER BY、分页子句,最终数据库只会返回符合条件的目标数据,不会加载全表内容,性能和直接返回实体的写法完全一致。
内容的提问来源于stack exchange,提问作者Sohaib Afzal
相关产品推荐
相关产品推荐

