У меня есть веб-поиск, но я все еще не могу найти простой ответ. Может кто-нибудь объяснить (на простом английском языке), что такое GroupJoin
? Чем он отличается от обычного внутреннего Join
? Используется ли он обычно? Это только для синтаксиса метода? Как насчет синтаксиса запроса? Пример кода С# был бы приятным.
Linq to Entities соединяются с groupjoin
Ответ 1
Поведение
Предположим, у вас есть два списка:
Id Value
1 A
2 B
3 C
Id ChildValue
1 a1
1 a2
1 a3
2 b1
2 b2
Когда вы Join
два списка в поле Id
, результат будет:
Value ChildValue
A a1
A a2
A a3
B b1
B b2
Когда вы GroupJoin
два списка в поле Id
, результат будет:
Value ChildValues
A [a1, a2, a3]
B [b1, b2]
C []
Итак, Join
создает плоский (табличный) результат родительских и дочерних значений. GroupJoin
создает список записей в первом списке, каждый из которых имеет группу объединенных записей во втором списке.
Вот почему Join
является эквивалентом INNER JOIN
в SQL: для C
нет записей. Пока GroupJoin
является эквивалентом OUTER JOIN
: C
находится в наборе результатов, но с пустым списком соответствующих записей (в наборе результатов SQL будет строка C - null
).
Синтаксис
Итак, пусть два списка будут IEnumerable<Parent>
и IEnumerable<Child>
соответственно. (В случае Linq to Entities: IQueryable<T>
).
Join
синтаксис будет
from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }
возвращает IEnumerable<X>
, где X является анонимным типом с двумя свойствами, Value
и ChildValue
. Этот синтаксис запроса использует метод Join
под капотом.
GroupJoin
синтаксис будет
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
возвращает IEnumerable<Y>
, где Y - анонимный тип, состоящий из одного свойства типа Parent
и свойства типа IEnumerable<Child>
. Этот синтаксис запроса использует метод GroupJoin
под капотом.
Мы могли бы просто сделать select g
в последнем запросе, который выберет IEnumerable<IEnumerable<Child>>
, скажем, список списков. Во многих случаях выбор с включенным родителем является более полезным.
Некоторые примеры использования
1. Изготовление плоского внешнего соединения.
Как сказано, утверждение...
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
... создает список родителей с дочерними группами. Это можно превратить в плоский список пар родитель-ребенок двумя небольшими дополнениями:
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 }
Результат похож на
Value Child
A a1
A a2
A a3
B b1
B b2
C (null)
Обратите внимание, что переменная диапазона C
повторно используется в приведенном выше описании. Для этого любой оператор Join
можно просто преобразовать в OUTER JOIN
, добавив эквивалент into g from c in g.DefaultIfEmpty()
в существующий оператор Join
.
Здесь синтаксис синтаксиса запроса (или полного). Синтаксис метода (или свободного) показывает, что на самом деле происходит, но его трудно написать:
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 } )
Итак, плоская OUTER JOIN
в LINQ - это GroupJoin
, сплющенная SelectMany
.
2. Заказ на сохранение
Предположим, что список родителей немного длиннее. В некоторых пользовательских интерфейсах создается список выбранных родителей как Id
в фиксированном порядке. Позвольте использовать:
var ids = new[] { 3,7,2,4 };
Теперь выбранные родители должны быть отфильтрованы из списка родителей в этом точном порядке.
Если мы делаем...
var result = parents.Where(p => ids.Contains(p.Id));
... порядок parents
определит результат. Если родители заказаны Id
, результатом будут родители 2, 3, 4, 7. Нехорошо. Однако мы можем использовать Join
для фильтрации списка. И используя ids
в качестве первого списка, порядок будет сохранен:
from id in ids
join p in parents on id equals p.Id
select p
В результате родители 3, 7, 2, 4.
Ответ 2
Согласно eduLINQ:
Лучший способ справиться с тем, что делает GroupJoin, - это думать о Присоединиться. Там общая идея заключалась в том, что мы просмотрели "внешний", входной последовательности, нашел все соответствующие элементы из "внутренней" последовательности (основанный на ключевой проекции на каждую последовательность), а затем дал пары соответствующие элементы. GroupJoin похож, за исключением того, что вместо получая пары элементов, он дает единственный результат для каждого "внешнего" элемент на основе этого элемента и последовательность совпадающих "внутренних" элементов.
Единственное различие заключается в операторе return:
Join
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]);
}
Подробнее здесь: