Linq to Entities Join vs Groupjoin

180

Ich habe im Internet gesucht, kann aber immer noch keine einfache Antwort finden. Kann jemand bitte (in einfachem Englisch) erklären, was ein GroupJoinist? Wie unterscheidet es sich von einem normalen Inneren Join? Wird es häufig verwendet? Ist es nur für die Methodensyntax? Was ist mit der Abfragesyntax? Ein c # -Codebeispiel wäre schön.

duyn9uyen
quelle
Laut MSDN ist ein Gruppen-Join eine Join-Klausel mit einem In-Ausdruck. Die Join-Klausel enthält weitere Informationen und Codebeispiele. Es handelt sich im Wesentlichen um eine innere Verknüpfung (wenn keine Elemente auf der rechten Seite mit den Elementen auf der linken Seite übereinstimmen, erhalten Sie ein Null-Ergebnis). Das Ergebnis ist jedoch in Gruppen organisiert.
Tim

Antworten:

370

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.

Gert Arnold
quelle
In einem GroupJoin enthalten die untergeordneten Werte also Objekte, die die zugehörigen Werte enthalten.
duyn9uyen
Wie Sie sagten, ist GroupJoin wie ein Outer Join, aber diese Syntax (rein linq für Group Join) besagt, dass es nicht wie Outer Join, sondern Left Outer Join ist.
Imad
2
Ich denke, ich würde angeben, dass der "Flat Outer Join" ein Left Outer Join ist.
NetMage
1
Perfekt erklärt, verstehe ich jetzt
Peterincumbria
19

Laut eduLINQ :

Der beste Weg, um sich mit GroupJoin vertraut zu machen, ist, an Join zu denken. Dort bestand die Gesamtidee darin, dass wir die "äußere" Eingabesequenz durchgesehen, alle übereinstimmenden Elemente aus der "inneren" Sequenz gefunden haben (basierend auf einer Schlüsselprojektion für jede Sequenz) und dann Paare von übereinstimmenden Elementen erhalten haben. GroupJoin ist ähnlich, mit der Ausnahme, dass anstelle von Elementpaaren ein einzelnes Ergebnis für jedes "äußere" Element basierend auf diesem Element und der Reihenfolge der übereinstimmenden "inneren" Elemente erzielt wird .

Der einzige Unterschied besteht in der Rückgabeerklärung:

Mitmachen :

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    foreach (var innerElement in lookup[key]) 
    { 
        yield return resultSelector(outerElement, innerElement); 
    } 
} 

GroupJoin :

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    yield return resultSelector(outerElement, lookup[key]); 
} 

Lesen Sie hier mehr:

MarcinJuraszek
quelle