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.
For example, a class or a relational database table named Student
might contain two fields: Id
and Name
. A second class or relational database table named Course
might contain two fields: StudentId
and CourseTitle
. 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).
Note
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.
Warning
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. For more information, see Join Operations and Entity Framework Core, GroupJoin.
The first example in this article shows you how to perform a group join. The second example shows you how to use a group join to create XML elements.
Note
The examples in this topic use the Person
and Pet
data classes from Perform inner joins.
Example - Group join
The following example performs a group join of objects of type Person
and Pet
based on the Person
matching the Pet.Owner
property. 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. The corresponding elements from the second collection, which in this example are Pet
objects, are grouped into a collection. Finally, the result selector function creates an anonymous type for each match that consists of Person.FirstName
and a collection of Pet
objects.
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:
*/
Example - Group join to create XML
Group joins are ideal for creating XML by using LINQ to 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.
// 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>
*/
See also
Feedback
Submit and view feedback for