执行分组联接

分组联接对于生成分层数据结构十分有用。 它将第一个集合中的每个元素与第二个集合中的一组相关元素进行配对。

例如,一个名为 Student 的类或关系数据库表可能包含两个字段:IdName。 另一个名为 Course 的类或关系数据库表可能包含两个字段:StudentIdCourseTitle。 这两个数据源的分组联接(基于匹配 Student.IdCourse.StudentId)会对具有 Course 对象集合(可能为空)的每个 Student 进行分组。

注意

第一个集合的每个元素都会出现在分组联接的结果集中(无论是否在第二个集合中找到关联元素)。 在未找到任何相关元素的情况下,该元素的相关元素序列为空。 因此,结果选择器有权访问第一个集合的每个元素。 这与非分组联接中的结果选择器不同,后者无法访问第一个集合中在第二个集合中没有匹配项的元素。

警告

Enumerable.GroupJoin 在传统关系数据库术语中没有直接等效项。 但是,此方法实现了内部联接和左外部联接的超集。 这两个操作都可以按照分组联接进行编写。 有关详细信息,请参阅联接操作Entity Framework Core,GroupJoin

本文的第一个示例演示如何执行分组联接。 第二个示例演示如何使用分组联接创建 XML 元素。

备注

本主题中的示例使用执行内部联接中的 PersonPet 数据类。

示例 - 分组联接

下面的示例基于与 Pet.Owner 属性匹配的 Person,来执行类型 PersonPet 的对象的分组联接。 与非分组联接(会为每个匹配生成元素对)不同,分组联接只为第一个集合的每个元素生成一个结果对象(在此示例中为 Person 对象)。 第二个集合中的对应元素(在此示例中为 Pet 对象)会分组到集合中。 最后,结果选择器函数会为每个匹配都创建一种匿名类型,其中包含 Person.FirstNamePet 对象集合。

Person magnus = new(FirstName: "Magnus", LastName: "Hedlund");
Person terry = new("Terry", "Adams");
Person charlotte = new("Charlotte", "Weiss");
Person arlene = new("Arlene", "Huff");

List<Person> people = new() { magnus, terry, charlotte, arlene };

List<Pet> pets = new()
{
    new(Name: "Barley", Owner: terry),
    new("Boots", terry),
    new("Whiskers", charlotte),
    new("Blue Moon", terry),
    new("Daisy", magnus),
};

// Create a list where each element is an anonymous type
// that contains the person's first name and a collection of
// pets that are owned by them.
var query =
    from person in people
    join pet in pets on person equals pet.Owner into gj
    select new
    {
        OwnerName = person.FirstName,
        Pets = gj
    };

foreach (var v in query)
{
    // Output the owner's name.
    Console.WriteLine($"{v.OwnerName}:");

    // Output each of the owner's pet's names.
    foreach (var pet in v.Pets)
    {
        Console.WriteLine($"  {pet.Name}");
    }
}

/* Output:
     Magnus:
       Daisy
     Terry:
       Barley
       Boots
       Blue Moon
     Charlotte:
       Whiskers
     Arlene:
 */

示例 - 用于创建 XML 的分组联接

分组联接非常适合于使用 LINQ to XML 创建 XML。 下面的示例类似于上面的示例,不过结果选择器函数不会创建匿名类型,而是创建表示联接对象的 XML 元素。

// using System.Xml.Linq;

Person magnus = new(FirstName: "Magnus", LastName: "Hedlund");
Person terry = new("Terry", "Adams");
Person charlotte = new("Charlotte", "Weiss");
Person arlene = new("Arlene", "Huff");

List<Person> people = new() { magnus, terry, charlotte, arlene };

List<Pet> pets = new()
{
    new(Name: "Barley", Owner: terry),
    new("Boots", terry),
    new("Whiskers", charlotte),
    new("Blue Moon", terry),
    new("Daisy", magnus),
};

XElement ownersAndPets = new("PetOwners",
    from person in people
    join pet in pets on person equals pet.Owner into gj
    select new XElement("Person",
        new XAttribute("FirstName", person.FirstName),
        new XAttribute("LastName", person.LastName),
        from subpet in gj
        select new XElement("Pet", subpet.Name)
    )
);

Console.WriteLine(ownersAndPets);

/* Output:
     <PetOwners>
       <Person FirstName="Magnus" LastName="Hedlund">
         <Pet>Daisy</Pet>
       </Person>
       <Person FirstName="Terry" LastName="Adams">
         <Pet>Barley</Pet>
         <Pet>Boots</Pet>
         <Pet>Blue Moon</Pet>
       </Person>
       <Person FirstName="Charlotte" LastName="Weiss">
         <Pet>Whiskers</Pet>
       </Person>
       <Person FirstName="Arlene" LastName="Huff" />
     </PetOwners>
*/

请参阅