This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Download Microsoft Edge
More info about Internet Explorer and Microsoft Edge
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).
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.
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
GroupJoin
Perform inner joins
Perform left outer joins
Anonymous types