You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

关键说明

  1. 避免循环查询:通过分组预处理将多条件合并为一次数据库查询,保证分页结果的正确性。
  2. 投影替代Include:EF Core的Include不支持针对不同父实体的动态子实体筛选,投影可以灵活处理每个Person的ContactInfo筛选逻辑。
  3. 空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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:38:17