Linq to Entities ingressar vs groupjoin

182

Eu pesquisei na web, mas ainda não consigo encontrar uma resposta simples. Alguém pode explicar (em inglês simples) o que GroupJoiné isso? Como é diferente de um interior regular Join? É comumente usado? É apenas para sintaxe de método? E a sintaxe da consulta? Um exemplo de código c # seria bom.

duyn9uyen
fonte
De acordo com o MSDN, uma associação ao grupo é uma cláusula de associação com uma expressão into. A cláusula join possui mais informações e exemplos de código. É essencialmente uma junção interna (se nenhum elemento da direita corresponder a nenhum da esquerda, você obtém um resultado nulo); no entanto, o resultado é organizado em grupos.
Tim

Respostas:

375

Comportamento

Suponha que você tenha duas listas:

Id  Value
1   A
2   B
3   C

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

Quando você Joinas duas listas no Idcampo, o resultado será:

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

Quando você GroupJoinas duas listas no Idcampo, o resultado será:

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

Portanto, Joinproduz um resultado simples (tabular) dos valores pai e filho.
GroupJoinproduz uma lista de entradas na primeira lista, cada uma com um grupo de entradas unidas na segunda lista.

É por isso que Joiné equivalente INNER JOINno SQL: não há entradas para C. Enquanto GroupJoiné o equivalente a OUTER JOIN: Cestá no conjunto de resultados, mas com uma lista vazia de entradas relacionadas (em um conjunto de resultados SQL, haveria uma linha C - null).

Sintaxe

Então deixe as duas listas serem IEnumerable<Parent>e IEnumerable<Child>respectivamente. (No caso de Linq to Entities:) IQueryable<T>.

Join sintaxe seria

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

retornando um IEnumerable<X>onde X é um tipo anônimo com duas propriedades Valuee ChildValue. Essa sintaxe de consulta usa o Joinmétodo sob o capô.

GroupJoin sintaxe seria

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

retornando um IEnumerable<Y>onde Y é um tipo anônimo que consiste em uma propriedade do tipo Parente uma propriedade do tipo IEnumerable<Child>. Essa sintaxe de consulta usa o GroupJoinmétodo sob o capô.

Poderíamos fazer select gna última consulta, que selecionaria uma IEnumerable<IEnumerable<Child>>, digamos uma lista de listas. Em muitos casos, a seleção com o pai incluído é mais útil.

Alguns casos de uso

1. Produzindo uma junção externa plana.

Como disse, a declaração ...

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

... produz uma lista de pais com grupos de crianças. Isso pode ser transformado em uma lista simples de pares pai-filho por duas pequenas adições:

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 }

O resultado é semelhante ao

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

Observe que a variável range c é reutilizada na instrução acima. Fazendo isso, qualquer joininstrução pode simplesmente ser convertida em uma outer joinadicionando o equivalente into g from c in g.DefaultIfEmpty()a uma joininstrução existente .

É aqui que a sintaxe da consulta (ou abrangente) brilha. A sintaxe do método (ou fluente) mostra o que realmente acontece, mas é difícil escrever:

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

Portanto, um apartamento outer joinno LINQ é um GroupJoin, achatado por SelectMany.

2. Preservando a ordem

Suponha que a lista de pais seja um pouco mais longa. Algumas UI produzem uma lista de pais selecionados como Idvalores em uma ordem fixa. Vamos usar:

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

Agora os pais selecionados devem ser filtrados da lista de pais nesta ordem exata.

Se nós fizermos ...

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

... a ordem de parentsdeterminará o resultado. Se os pais são ordenados por Id, o resultado será os pais 2, 3, 4, 7. Não é bom. No entanto, também podemos usar joinpara filtrar a lista. E, usando idscomo primeira lista, o pedido será preservado:

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

O resultado são os pais 3, 7, 2, 4.

Gert Arnold
fonte
Então, em um GroupJoin, os valores filhos conterão objetos que contêm os valores relacionados?
duyn9uyen
Como você disse, GroupJoin é como uma junção externa, mas essa sintaxe (puramente linq para junção de grupo) diz que não é como junção externa, mas deixou a junção externa.
Imad
2
Acho que especificaria que a "Junção externa plana" é uma Junção externa esquerda.
NetMage
1
Explicou perfeitamente, agora eu entendo
peterincumbria
19

De acordo com o eduLINQ :

A melhor maneira de entender o que o GroupJoin faz é pensar em Join. Lá, a ideia geral era que examinássemos a sequência de entrada "externa", encontrássemos todos os itens correspondentes da sequência "interna" (com base em uma projeção chave em cada sequência) e, então, produzíssemos pares de elementos correspondentes. GroupJoin é semelhante, exceto que, em vez de produzir pares de elementos, gera um único resultado para cada item "externo" com base nesse item e na sequência de itens "internos" correspondentes .

A única diferença está na declaração de retorno:

Inscreva - se :

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

Grupo :

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

Leia mais aqui:

MarcinJuraszek
fonte