Linq to Entities si unisce a groupjoin


182

Ho cercato sul web ma non riesco ancora a trovare una risposta semplice. Qualcuno può spiegare (in un inglese semplice) che cos'è un GroupJoin? In che cosa differisce da un interno normale Join? È comunemente usato? È solo per la sintassi del metodo? Che dire della sintassi delle query? Un esempio di codice c # sarebbe bello.


Secondo MSDN, un join di gruppo è una clausola di join con un'espressione in. La clausola join contiene ulteriori informazioni e esempi di codice. È essenzialmente un join interno (se nessun elemento nella destra corrisponde a nessuno nella sinistra, si ottiene un risultato nullo); tuttavia il risultato è organizzato in gruppi.
Tim

Risposte:


375

Comportamento

Supponiamo di avere due elenchi:

Id  Value
1   A
2   B
3   C

Id  ChildValue
1   a1
1   a2
1   a3
2   b1
2   b2

Quando Joinle due liste sul Idcampo il risultato sarà:

Value ChildValue
A     a1
A     a2
A     a3
B     b1
B     b2

Quando GroupJoinle due liste sul Idcampo il risultato sarà:

Value  ChildValues
A      [a1, a2, a3]
B      [b1, b2]
C      []

Quindi Joinproduce un risultato piatto (tabulare) dei valori padre e figlio.
GroupJoinproduce un elenco di voci nel primo elenco, ognuna con un gruppo di voci unite nel secondo elenco.

Ecco perché Joinè l'equivalente di INNER JOINin SQL: non ci sono voci per C. While GroupJoinè l'equivalente di OUTER JOIN: Cè nel set di risultati, ma con un elenco vuoto di voci correlate (in un set di risultati SQL ci sarebbe una riga C - null).

Sintassi

Quindi lascia che le due liste siano IEnumerable<Parent>e IEnumerable<Child>rispettivamente. (Nel caso di Linq alle Entità:) IQueryable<T>.

Join la sintassi sarebbe

from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }

restituendo un IEnumerable<X>dove X è un tipo anonimo con due proprietà Valuee ChildValue. Questa sintassi della query utilizza il Joinmetodo sottostante.

GroupJoin la sintassi sarebbe

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

restituendo un IEnumerable<Y>dove Y è un tipo anonimo costituito da una proprietà di tipo Parente una proprietà di tipo IEnumerable<Child>. Questa sintassi della query utilizza il GroupJoinmetodo sottostante.

Potremmo fare solo select gin quest'ultima query, che selezionerebbe un IEnumerable<IEnumerable<Child>>, diciamo un elenco di elenchi. In molti casi è più utile selezionare con il genitore incluso.

Alcuni casi d'uso

1. Produzione di un join esterno piatto.

Come detto, la dichiarazione ...

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

... produce un elenco di genitori con gruppi di bambini. Questo può essere trasformato in un elenco semplice di coppie genitore-figlio con due piccole aggiunte:

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 }

Il risultato è simile a

Value Child
A     a1
A     a2
A     a3
B     b1
B     b2
C     (null)

Si noti che la variabile intervallo c viene riutilizzata nell'istruzione precedente. In questo modo, qualsiasi joinistruzione può essere semplicemente convertita in an outer joinaggiungendo l'equivalente di into g from c in g.DefaultIfEmpty()a un'istruzione esistente join.

Qui è dove brilla la sintassi della query (o completa). La sintassi del metodo (o fluente) mostra cosa succede realmente, ma è difficile scrivere:

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 } )

Quindi un appartamento outer joinin LINQ è un GroupJoin, appiattito da SelectMany.

2. Ordine di conservazione

Supponiamo che l'elenco dei genitori sia un po 'più lungo. Alcune UI producono un elenco di genitori selezionati come Idvalori in un ordine fisso. Usiamo:

var ids = new[] { 3,7,2,4 };

Ora i genitori selezionati devono essere filtrati dall'elenco dei genitori in questo preciso ordine.

Se lo facciamo ...

var result = parents.Where(p => ids.Contains(p.Id));

... l'ordine di parentsdeterminerà il risultato. Se i genitori sono ordinati per Id, il risultato saranno i genitori 2, 3, 4, 7. Non va bene. Tuttavia, possiamo anche utilizzare joinper filtrare l'elenco. E usando idscome primo elenco, l'ordine verrà preservato:

from id in ids
join p in parents on id equals p.Id
select p

Il risultato sono i genitori 3, 7, 2, 4.


Quindi in un GroupJoin, i valori figlio conterranno oggetti che contengono i valori correlati?
duyn9uyen,

Come hai detto, GroupJoin è come un join esterno ma quella sintassi (puramente linq per un join di gruppo) dice che non è come un join esterno ma un join esterno sinistro.
Imad,

2
Penso che vorrei specificare che il "Join esterno piatto" è un Join esterno sinistro.
NetMage,

1
Spiegato perfettamente, ora capisco
peterincumbria,

19

Secondo eduLINQ :

Il modo migliore per capire cosa fa GroupJoin è pensare a Join. Lì, l'idea generale era che abbiamo guardato attraverso la sequenza di input "esterna", trovato tutti gli elementi corrispondenti dalla sequenza "interna" (sulla base di una proiezione chiave su ciascuna sequenza) e quindi prodotto coppie di elementi corrispondenti. GroupJoin è simile, tranne per il fatto che invece di generare coppie di elementi, produce un singolo risultato per ogni articolo "esterno" basato su quell'elemento e sulla sequenza di oggetti "interni" corrispondenti .

L'unica differenza è nella dichiarazione di ritorno:

Partecipa :

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]); 
} 

Leggi di più qui:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.