Verhalten
Angenommen, Sie haben zwei Listen:
Id Value
1 A
2 B
3 C
Id ChildValue
1 a1
1 a2
1 a3
2 b1
2 b2
Wenn Sie Joindie beiden Listen auf dem IdFeld haben, wird das Ergebnis sein:
Value ChildValue
A a1
A a2
A a3
B b1
B b2
Wenn Sie GroupJoindie beiden Listen auf dem IdFeld haben, wird das Ergebnis sein:
Value ChildValues
A [a1, a2, a3]
B [b1, b2]
C []
So Joinergibt sich eine flache (tabellarische) Ergebnis von Eltern und Kind Werte.
GroupJoinErzeugt eine Liste von Einträgen in der ersten Liste mit jeweils einer Gruppe von verbundenen Einträgen in der zweiten Liste.
Aus diesem Grund Joinentspricht dies INNER JOINSQL: Es gibt keine Einträge für C. Während GroupJoinist das Äquivalent von OUTER JOIN: Cist in der Ergebnismenge, aber mit einer leeren Liste verwandter Einträge (in einer SQL-Ergebnismenge würde es eine Zeile geben C - null).
Syntax
Also lassen Sie die beiden Listen sein IEnumerable<Parent>und IEnumerable<Child>jeweils. (Im Fall von Linq to Entities :) IQueryable<T>.
Join Syntax wäre
from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }
Rückgabe eines IEnumerable<X>wobei X ein anonymer Typ mit zwei Eigenschaften ist, Valueund ChildValue. Diese Abfragesyntax verwendet die JoinMethode unter der Haube.
GroupJoin Syntax wäre
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
Rückgabe eines IEnumerable<Y>wobei Y ein anonymer Typ ist, der aus einer Eigenschaft des Typs Parentund einer Eigenschaft des Typs besteht IEnumerable<Child>. Diese Abfragesyntax verwendet die GroupJoinMethode unter der Haube.
Wir könnten einfach select gin der letzteren Abfrage IEnumerable<IEnumerable<Child>>eine Liste von Listen auswählen . In vielen Fällen ist die Auswahl mit dem übergeordneten Element nützlicher.
Einige Anwendungsfälle
1. Herstellen einer flachen äußeren Verbindung.
Wie gesagt, die Aussage ...
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
... erstellt eine Liste der Eltern mit Kindergruppen. Dies kann durch zwei kleine Ergänzungen in eine flache Liste von Eltern-Kind-Paaren umgewandelt werden:
from p in parents
join c in children on p.Id equals c.Id into g // <= into
from c in g.DefaultIfEmpty() // <= flattens the groups
select new { Parent = p.Value, Child = c?.ChildValue }
Das Ergebnis ist ähnlich wie
Value Child
A a1
A a2
A a3
B b1
B b2
C (null)
Beachten Sie, dass die Bereichsvariable c in der obigen Anweisung wiederverwendet wird. Auf diese Weise kann jede joinAnweisung einfach in eine konvertiert werden, outer joinindem das Äquivalent into g from c in g.DefaultIfEmpty()zu einer vorhandenen joinAnweisung hinzugefügt wird .
Hier setzt die Abfragesyntax (oder umfassende Syntax) an. Die Methodensyntax (oder die fließende Syntax) zeigt, was wirklich passiert, aber es ist schwer zu schreiben:
parents.GroupJoin(children, p => p.Id, c => c.Id, (p, c) => new { p, c })
.SelectMany(x => x.c.DefaultIfEmpty(), (x,c) => new { x.p.Value, c?.ChildValue } )
Eine Wohnung outer joinin LINQ ist also eine GroupJoin, abgeflacht von SelectMany.
2. Ordnung bewahren
Angenommen, die Liste der Eltern ist etwas länger. Einige Benutzeroberflächen erstellen eine Liste ausgewählter Eltern als IdWerte in einer festen Reihenfolge. Verwenden wir:
var ids = new[] { 3,7,2,4 };
Jetzt müssen die ausgewählten Eltern in genau dieser Reihenfolge aus der Elternliste herausgefiltert werden.
Wenn wir es tun ...
var result = parents.Where(p => ids.Contains(p.Id));
... bestimmt die Reihenfolge des parentsWillens das Ergebnis. Wenn die Eltern von bestellt werden Id, sind die Eltern 2, 3, 4, 7. Das Ergebnis ist nicht gut. Wir können jedoch auch joindie Liste filtern. Durch die Verwendung idsals erste Liste bleibt die Reihenfolge erhalten:
from id in ids
join p in parents on id equals p.Id
select p
Das Ergebnis sind die Eltern 3, 7, 2, 4.