如何通过LINQ Join填充子列表?求技术实现指引
用LINQ实现子列表填充的方案
我来帮你梳理下用LINQ关联并填充子列表的思路,结合你提到的Personel(人员)、Contact(联系人)、ContactType(联系人类型)的一对多关联场景,咱们一步步来:
先明确类结构(基于你的描述假设)
首先先把常见的领域类和DTO类结构列出来,方便后续代码示例理解:
// 领域类 public class Personel { public int PersonelId { get; set; } public string Name { get; set; } public List<Contact> Contacts { get; set; } = new List<Contact>(); } public class Contact { public int ContactId { get; set; } public int PersonelId { get; set; } public string Value { get; set; } // 比如手机号、邮箱 public ContactType Type { get; set; } } public class ContactType { public int TypeId { get; set; } public string TypeName { get; set; } // 比如"手机"、"邮箱" } // DTO类(用于前端展示或数据传输) public class PersonelDTO { public int Id { get; set; } public string FullName { get; set; } public List<ContactDTO> ContactList { get; set; } = new List<ContactDTO>(); } public class ContactDTO { public string ContactValue { get; set; } public string ContactTypeName { get; set; } }
方案1:用GroupJoin实现(推荐一对多场景)
GroupJoin天生就是为一对多关联设计的,它会把每个主表(Personel)元素和对应的子表(关联了ContactType的Contact)元素组绑定,而且默认是左外连接,即使某个Personel没有联系人也会被保留:
// 假设你有这些数据源 List<Personel> personels = GetPersonelsFromDB(); List<Contact> contacts = GetContactsFromDB(); List<ContactType> contactTypes = GetContactTypesFromDB(); // 先把Contact和ContactType关联,得到带类型名称的联系人集合 var contactsWithType = contacts.Join( contactTypes, contact => contact.Type.TypeId, type => type.TypeId, (contact, type) => new { contact.PersonelId, contact.Value, type.TypeName } ); // 再用GroupJoin把Personel和带类型的联系人组关联,填充DTO var personelDTOs = personels .GroupJoin( contactsWithType, personel => personel.PersonelId, contact => contact.PersonelId, (personel, relatedContacts) => new PersonelDTO { Id = personel.PersonelId, FullName = personel.Name, ContactList = relatedContacts.Select(c => new ContactDTO { ContactValue = c.Value, ContactTypeName = c.TypeName }).ToList() } ) .ToList();
方案2:用Join+GroupBy实现(多表关联更灵活)
如果需要关联更多表或者先做过滤操作,也可以先把所有表Join起来,再按Personel分组,最后填充子列表:
var personelDTOs = personels // 第一步:关联Personel和Contact .Join(contacts, p => p.PersonelId, c => c.PersonelId, (p, c) => new { Personel = p, Contact = c }) // 第二步:关联上ContactType .Join(contactTypes, pc => pc.Contact.Type.TypeId, ct => ct.TypeId, (pc, ct) => new { pc.Personel, pc.Contact, ContactType = ct }) // 第三步:按PersonelId分组,把同一个人的联系人归为一组 .GroupBy(x => x.Personel.PersonelId) // 第四步:转换为DTO,填充子列表 .Select(g => new PersonelDTO { Id = g.Key, FullName = g.First().Personel.Name, ContactList = g.Select(item => new ContactDTO { ContactValue = item.Contact.Value, ContactTypeName = item.ContactType.TypeName }).ToList() }) .ToList();
注意:如果要保留无联系人的Personel
上面的Join+GroupBy默认是内连接,没有联系人的Personel会被过滤掉。如果要保留它们,需要改成左外连接,结合DefaultIfEmpty():
var personelDTOs = personels // 左外连接Personel和Contact .GroupJoin(contacts, p => p.PersonelId, c => c.PersonelId, (p, cs) => new { Personel = p, Contacts = cs.DefaultIfEmpty() }) // 展开联系人集合(处理空的情况) .SelectMany(pc => pc.Contacts.Select(c => new { pc.Personel, Contact = c })) // 左外连接ContactType .GroupJoin(contactTypes, pc => pc.Contact?.Type.TypeId, ct => ct.TypeId, (pc, cts) => new { pc.Personel, pc.Contact, ContactTypes = cts.DefaultIfEmpty() }) .SelectMany(pct => pct.ContactTypes.Select(ct => new { pct.Personel, pct.Contact, ContactType = ct })) // 分组并转换DTO,过滤掉空联系人的情况 .GroupBy(x => x.Personel.PersonelId) .Select(g => new PersonelDTO { Id = g.Key, FullName = g.First().Personel.Name, ContactList = g.Where(item => item.Contact != null) .Select(item => new ContactDTO { ContactValue = item.Contact.Value, ContactTypeName = item.ContactType?.TypeName ?? "未知类型" }).ToList() }) .ToList();
额外提醒
如果你的数据是来自EF Core等ORM框架,其实可以直接用Include和ThenInclude加载关联数据,不需要手动写Join:
var personelsWithContacts = await _dbContext.Personels .Include(p => p.Contacts) .ThenInclude(c => c.Type) .ToListAsync(); // 之后再转换为DTO即可
内容的提问来源于stack exchange,提问作者klevendoglu
相关产品推荐
相关产品推荐

