EF Core 从指定列表获取实体及子实体并返回分页结果
解决方案:EF Core实现带ContactInfo筛选的Person分页查询
针对你的需求,我们可以通过分组处理请求条件+投影筛选子实体的方式实现,既规避EF Core对Include筛选的限制,也能生成正确的分页结果。
步骤1:预处理请求条件
先将传入的request.Items按PersonId分组,记录每个Person对应的筛选规则:
// 分组整理:Key=PersonId,Value=包含筛选规则的对象 var personFilterGroups = request.Items .GroupBy(item => item.PersonId) .ToDictionary( group => group.Key, group => new { // 收集非空的ContactId TargetContactIds = group.Select(item => item.ContactId) .Where(id => id != null) .ToList(), // 判断是否需要返回该Person的全部ContactInfo IncludeAllContacts = group.Any(item => item.ContactId == null) } );
步骤2:构建查询并筛选子实体
用EF Core的投影(Select)替代Include处理ContactInfo筛选,绕过EF Core对Include的限制:
// 基础查询:先过滤出符合条件的Person var baseQuery = _context.Person .AsNoTracking() .Where(person => personFilterGroups.ContainsKey(person.Id)); // 投影查询:按规则筛选ContactInfo并映射到DTO var query = baseQuery.Select(person => new PeopleDto { Id = person.Id, // 映射Person的其他字段... ContactInfos = personFilterGroups[person.Id].IncludeAllContacts ? // 返回全部ContactInfo person.ContactInfos.Select(contact => new ContactInfoDto { Id = contact.Id, Phone = contact.Phone // 映射ContactInfo的其他字段... }).ToList() : // 只返回指定ContactId的条目 person.ContactInfos .Where(contact => personFilterGroups[person.Id].TargetContactIds.Contains(contact.Id)) .Select(contact => new ContactInfoDto { Id = contact.Id, Phone = contact.Phone // 映射ContactInfo的其他字段... }).ToList() });
步骤3:执行分页
基于上述查询完成分页,总条数取符合条件的Person去重数量:
// 获取总条数 var totalCount = await baseQuery.CountAsync(cancellationToken); // 分页查询 var paginatedItems = await query .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(cancellationToken); // 返回分页结果(假设PaginatedList是你的分页DTO) return new PaginatedList<PeopleDto>(paginatedItems, totalCount, request.Page, request.PageSize);
关键说明
- 避免循环查询:通过分组预处理将多条件合并为一次数据库查询,保证分页结果的正确性。
- 投影替代Include:EF Core的
Include不支持针对不同父实体的动态子实体筛选,投影可以灵活处理每个Person的ContactInfo筛选逻辑。 - 空ContactId处理:通过
IncludeAllContacts标记,确保当存在空ContactId条目时,返回该Person的全部ContactInfo。
替代方案:结合AutoMapper ProjectTo
如果你习惯用AutoMapper,可先处理筛选再投影:
var query = baseQuery .Select(person => new { Person = person, FilteredContacts = personFilterGroups[person.Id].IncludeAllContacts ? person.ContactInfos : person.ContactInfos.Where(c => personFilterGroups[person.Id].TargetContactIds.Contains(c.Id)) }) .ProjectTo<PeopleDto>(_mapper.ConfigurationProvider);
内容的提问来源于stack exchange,提问作者Sergio
相关产品推荐
相关产品推荐

