Linq to Entities соединяются с groupjoin

У меня есть веб-поиск, но я все еще не могу найти простой ответ. Может кто-нибудь объяснить (на простом английском языке), что такое GroupJoin ? Как он отличается от обычного внутреннего соединения? Используется ли он обычно? Это только для синтаксиса метода? Как насчет синтаксиса запроса? Пример кода c # был бы приятным.

Поведение

Предположим, у вас есть два списка:

 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 и IEnumerable соответственно. (В случае Linq to Entities: IQueryable ).

Синтаксис соединения будет

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

возвращая IEnumerable где 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 – анонимный тип, состоящий из одного свойства типа Parent и свойства типа IEnumerable . Этот синтаксис запроса использует метод GroupJoin под капотом.

Мы могли бы просто select g в последнем запросе, который выберет IEnumerable> , скажем, список списков. Во многих случаях выбор с включенным родителем является более полезным.

Некоторые варианты использования

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 => xcDefaultIfEmpty(), (x,c) => new { xpValue, 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.

Согласно eduLINQ :

Лучший способ справиться с тем, что делает GroupJoin, – это думать о Join. Там общая идея заключалась в том, что мы просмотрели «внешнюю» входную последовательность, нашли все соответствующие элементы из «внутренней» последовательности (основанные на ключевой проекции на каждую последовательность), а затем дали пары совпадающих элементов. GroupJoin аналогичен, за исключением того, что вместо получения пар элементов он дает единственный результат для каждого «внешнего» элемента на основе этого элемента и последовательности совпадающих «внутренних» элементов .

Единственное различие заключается в следующем:

Присоединиться :

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

Подробнее здесь:

  • Повторное выполнение LINQ для объектов: часть 19 – Присоединение

  • Повторное выполнение LINQ для объектов: часть 22 – GroupJoin

  • LINQ to Entities не распознает метод
  • Как сделать массовую вставку - Linq для объектов
  • Метод не может быть переведен в выражение хранилища
  • Включить внуков в EF Query
  • Указанный член типа не поддерживается в LINQ to Entities. Поддерживаются только инициализаторы, сущности и свойства навигации сущности
  • Linq int to string
  • «Дата» не поддерживается в LINQ to Entities. Поддерживаются только инициализаторы, сущности и свойства навигации сущности
  • В чем разница между .ToList (), .AsEnumerable (), AsQueryable ()?
  • Сколько Include я могу использовать в ObjectSet в EntityFramework для сохранения производительности?
  • Зачем использовать LINQ Join для простого отношения «один-много»?
  • 'Содержит ()' обходной путь с использованием Linq для Entities?
  • Давайте будем гением компьютера.