Linq to Entities приєднується до groupjoin


182

Я шукав в Інтернеті, але все ще не можу знайти просту відповідь. Може хтось пояснить, будь ласка, простою англійською мовою, що GroupJoinтаке? Чим він відрізняється від звичайного внутрішнього Join? Чи часто використовується? Це лише для синтаксису методу? Що з синтаксисом запитів? Приклад коду c # був би непоганим.


Відповідно до MSDN, приєднання до групи - це приєднання з виразом. У пункті приєднання є більше інформації та зразки коду. Це по суті внутрішнє з'єднання (якщо жодна з елементів праворуч не відповідає жодній зліва, ви отримуєте нульовий результат); однак результат організований у групи.
Тим

Відповіді:


375

Поведінка

Припустимо, у вас є два списки:

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 JOINSQL: записів для C. Хоча GroupJoinеквівалент OUTER JOIN: Cзнаходиться у наборі результатів, але з порожнім списком пов’язаних записів (у наборі результатів SQL був би рядок C - null).

Синтаксис

Тож нехай два списки будуть IEnumerable<Parent>і IEnumerable<Child>відповідно. (У випадку з Linq до юридичних осіб:) IQueryable<T>.

Join синтаксис був би

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

повернення an, 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 }

повернення an, 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.


Отже, у GroupJoin дочірні значення будуть містити об'єкти, що містять пов'язані значення?
duyn9uyen

Як ви сказали, GroupJoin - це як зовнішнє з'єднання, але цей синтаксис (чисто linq для групового приєднання) говорить, що це не як зовнішнє з'єднання, а ліве зовнішнє з'єднання.
Імад

2
Думаю, я зазначив би, що "З'єднання плоских зовнішніх" - це з'єднання ліворуч.
NetMage

1
Пояснив ідеально, я зараз розумію
peterincumbria

19

Відповідно до 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]); 
} 

Детальніше читайте тут:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.