执行分组联接Perform grouped joins

分组联接对于生成分层数据结构十分有用。The group join is useful for producing hierarchical data structures. 它将第一个集合中的每个元素与第二个集合中的一组相关元素进行配对。It pairs each element from the first collection with a set of correlated elements from the second collection.

例如,一个名为 Student 的类或关系数据库表可能包含两个字段:IdNameFor example, a class or a relational database table named Student might contain two fields: Id and Name. 另一个名为 Course 的类或关系数据库表可能包含两个字段:StudentIdCourseTitleA second class or relational database table named Course might contain two fields: StudentId and CourseTitle. 这两个数据源的分组联接(基于匹配 Student.IdCourse.StudentId)会对具有 Course 对象集合(可能为空)的每个 Student 进行分组。A group join of these two data sources, based on matching Student.Id and Course.StudentId, would group each Student with a collection of Course objects (which might be empty).

备注

第一个集合的每个元素都会出现在分组联接的结果集中(无论是否在第二个集合中找到关联元素)。Each element of the first collection appears in the result set of a group join regardless of whether correlated elements are found in the second collection. 在未找到任何相关元素的情况下,该元素的相关元素序列为空。In the case where no correlated elements are found, the sequence of correlated elements for that element is empty. 因此,结果选择器有权访问第一个集合的每个元素。The result selector therefore has access to every element of the first collection. 这与非分组联接中的结果选择器不同,后者无法访问第一个集合中在第二个集合中没有匹配项的元素。This differs from the result selector in a non-group join, which cannot access elements from the first collection that have no match in the second collection.

警告

Enumerable.GroupJoin 在传统关系数据库术语中没有直接等效项。Enumerable.GroupJoin has no direct equivalent in traditional relational database terms. 但是,此方法实现了内部联接和左外部联接的超集。However, this method does implement a superset of inner joins and left outer joins. 这两个操作都可以按照分组联接进行编写。Both of these operations can be written in terms of a grouped join. 有关详细信息,请参阅联接操作Entity Framework Core,GroupJoinFor more information, see Join Operations and Entity Framework Core, GroupJoin.

本文的第一个示例演示如何执行分组联接。The first example in this article shows you how to perform a group join. 第二个示例演示如何使用分组联接创建 XML 元素。The second example shows you how to use a group join to create XML elements.

示例 - 分组联接Example - Group join

下面的示例基于与 Pet.Owner 属性匹配的 Person,来执行类型 PersonPet 的对象的分组联接。The following example performs a group join of objects of type Person and Pet based on the Person matching the Pet.Owner property. 与非分组联接(会为每个匹配生成元素对)不同,分组联接只为第一个集合的每个元素生成一个结果对象(在此示例中为 Person 对象)。Unlike a non-group join, which would produce a pair of elements for each match, the group join produces only one resulting object for each element of the first collection, which in this example is a Person object. 第二个集合中的对应元素(在此示例中为 Pet 对象)会分组到集合中。The corresponding elements from the second collection, which in this example are Pet objects, are grouped into a collection. 最后,结果选择器函数会为每个匹配都创建一种匿名类型,其中包含 Person.FirstNamePet 对象集合。Finally, the result selector function creates an anonymous type for each match that consists of Person.FirstName and a collection of Pet objects.

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

/// <summary>
/// This example performs a grouped join.
/// </summary>
public static void GroupJoinExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    // Create two lists.
    List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

    // 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 (Pet pet in v.Pets)
            Console.WriteLine($"  {pet.Name}");
    }
}

// This code produces the following output:
//
// Magnus:
//   Daisy
// Terry:
//   Barley
//   Boots
//   Blue Moon
// Charlotte:
//   Whiskers
// Arlene:

示例 - 用于创建 XML 的分组联接Example - Group join to create XML

分组联接非常适合于使用 LINQ to XML 创建 XML。Group joins are ideal for creating XML by using LINQ to XML. 下面的示例类似于上面的示例,不过结果选择器函数不会创建匿名类型,而是创建表示联接对象的 XML 元素。The following example is similar to the previous example except that instead of creating anonymous types, the result selector function creates XML elements that represent the joined objects.

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

/// <summary>
/// This example creates XML output from a grouped join.
/// </summary>
public static void GroupJoinXMLExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    // Create two lists.
    List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

    // Create XML to display the hierarchical organization of people and their pets.
    XElement ownersAndPets = new XElement("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);
}

// This code produces the following 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>

请参阅See also