.NET Core 3.1 EF优化LINQ查询后报无法翻译错误如何解决?
问题根因分析
- 你在
.Select()中主动构造了EF实体类Sale实例,EF Core无法将「构造实体+后续基于该实例属性做关联查询」的逻辑翻译为SQL,这是触发报错的直接原因。 - 你在投影操作前加的
.Include()完全无效,只要后续使用自定义投影,Include规则都会被EF Core自动忽略。 - 你在最终投影
SaleJoinDto时,直接对导航属性子集合调用.ToList(),同时在SaleProductServiceAreas的投影中嵌套关联ProductServiceAreas表,这类嵌套集合操作无法被直接翻译为SQL。 - 你的分页逻辑存在严重性能问题:先全量拉取所有数据到内存,再做排序分页,相当于完全没有用到数据库分页能力,比你原来的慢查询性能还要差。
修复后的代码
using (var context = new AppDbContext()) { var result = new PagedResult<SaleJoinDto>(); result.CurrentPage = pageNumber; result.PageSize = pageSize; var firstDayOfMonth = new DateTime(date.Year, date.Month, 1); var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); // 先做计数,不需要拉取所有数据 result.RowCount = await context.Sales .Where(x => x.Date >= firstDayOfMonth && x.Date <= lastDayOfMonth) .CountAsync(); var pageCount = (double)result.RowCount / pageSize; result.PageCount = (int)Math.Ceiling(pageCount); var skip = (pageNumber - 1) * pageSize; // 直接单查询投影,不需要中间构造实体 result.Results = await context.Sales .Where(x => x.Date >= firstDayOfMonth && x.Date <= lastDayOfMonth) .OrderByDescending(x => x.Date) .Skip(skip) .Take(pageSize) .Select(s => new SaleJoinDto() { SaleId = s.SaleId, CustomerId = s.CustomerId, Date = s.Date, Total = s.Total, Session = s.Session, // 直接通过导航属性取关联字段,不需要手动Join CustomerNameSurname = s.Customer.Name + " " + s.Customer.Surname, ProductServiceName = s.ProductService.Name, ProductServiceId = s.ProductServiceId, SalePayments = s.SalePayments.Select(x => new SalePaymentDto() { Date = x.Date, PaymentMethod = x.PaymentMethod, SaleId = x.SaleId, SalePaymentId = x.SalePaymentId, Total = x.Total }).ToList(), SaleProductServiceAreas = s.SaleProductServiceAreas.Select(sp => new SaleProductServiceAreaJoinDto() { SaleProductServiceAreaId = sp.SaleProductServiceAreaId, ProductServiceAreaName = sp.ProductServiceArea.Name, ProductServiceAreaId = sp.ProductServiceAreaId, SaleId = sp.SaleId }).ToList() }) .ToListAsync(); return result; }
额外优化说明
- 你的Sale实体如果已经配置了
Customer、ProductService、SalePayments、SaleProductServiceAreas这些导航属性,完全不需要手动写Join,EF Core会自动根据导航属性生成关联语句,代码可读性和翻译成功率都会高很多。 - 分页先做Count查询,再在数据库层面做Skip/Take,避免全量拉取数据。
- 如果你用的是EF Core 5+,上面的嵌套集合投影可以直接被翻译为SQL,不需要额外处理;如果版本低于5,可以先把主表数据查出来,再通过一次批量查询拉取关联的子集合数据做映射,避免N+1查询。
内容的提问来源于stack exchange,提问作者Avora
相关产品推荐
相关产品推荐

