Linq để các thực thể tham gia vs nhóm tham gia


182

Tôi đã tìm kiếm trên web nhưng tôi vẫn không thể tìm thấy một câu trả lời đơn giản. Ai đó có thể vui lòng giải thích (bằng tiếng Anh đơn giản) a GroupJoinlà gì không? Làm thế nào nó khác với một bên trong thông thường Join? Nó có được sử dụng phổ biến không? Có phải nó chỉ cho cú pháp phương thức? Thế còn cú pháp truy vấn? Một ví dụ mã c # sẽ tốt đẹp.


Theo MSDN, tham gia nhóm là một mệnh đề tham gia có biểu thức thành. điều khoản tham gia có nhiều thông tin và mẫu mã. Về cơ bản, đó là một phép nối bên trong (nếu không có phần tử nào ở bên phải khớp với bất kỳ phần nào ở bên trái, bạn sẽ nhận được kết quả null); tuy nhiên kết quả được tổ chức thành các nhóm.
Tim

Câu trả lời:


375

Hành vi

Giả sử bạn có hai danh sách:

Id  Value
1   A
2   B
3   C

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

Khi bạn Joinhai danh sách trên Idtrường, kết quả sẽ là:

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

Khi bạn GroupJoinhai danh sách trên Idtrường, kết quả sẽ là:

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

Vì vậy, Jointạo ra một kết quả phẳng (bảng) của các giá trị cha và con.
GroupJointạo một danh sách các mục trong danh sách đầu tiên, mỗi mục có một nhóm các mục đã tham gia trong danh sách thứ hai.

Đó là lý do tại sao Jointương đương với INNER JOINSQL: không có mục nào cho C. Trong khi GroupJointương đương với OUTER JOIN: Cnằm trong tập kết quả, nhưng với một danh sách trống các mục liên quan (trong tập kết quả SQL sẽ có một hàng C - null).

Cú pháp

Vì vậy, hãy để hai danh sách được IEnumerable<Parent>IEnumerable<Child>tương ứng. (Trong trường hợp Linq cho các thực thể IQueryable<T>:).

Join cú pháp sẽ là

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

trả về một IEnumerable<X>nơi X là một loại ẩn danh có hai thuộc tính ValueChildValue. Cú pháp truy vấn này sử dụng Joinphương thức dưới mui xe.

GroupJoin cú pháp sẽ là

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

trả về một IEnumerable<Y>trong đó Y là một loại ẩn danh bao gồm một thuộc tính loại Parentvà thuộc tính loại IEnumerable<Child>. Cú pháp truy vấn này sử dụng GroupJoinphương thức dưới mui xe.

Chúng ta chỉ có thể thực hiện select gtrong truy vấn sau, trong đó sẽ chọn IEnumerable<IEnumerable<Child>>một danh sách các danh sách. Trong nhiều trường hợp, lựa chọn có cha mẹ đi kèm là hữu ích hơn.

Một số trường hợp sử dụng

1. Sản xuất một liên kết bên ngoài bằng phẳng.

Như đã nói, tuyên bố ...

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

... Tạo ra một danh sách các bậc cha mẹ với các nhóm trẻ. Điều này có thể được biến thành một danh sách phẳng các cặp cha-con bằng hai bổ sung nhỏ:

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 }

Kết quả tương tự như

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

Lưu ý rằng biến phạm vi c được sử dụng lại trong câu lệnh trên. Làm điều này, bất kỳ joincâu lệnh nào cũng có thể được chuyển đổi thành một outer joinbằng cách thêm tương đương into g from c in g.DefaultIfEmpty()với một joincâu lệnh hiện có .

Đây là nơi cú pháp truy vấn (hoặc toàn diện) tỏa sáng. Cú pháp phương thức (hoặc trôi chảy) cho thấy những gì thực sự xảy ra, nhưng thật khó để viết:

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

Vì vậy, một căn hộ outer jointrong LINQ là một GroupJoin, làm phẳng bởi SelectMany.

2. Giữ gìn trật tự

Giả sử danh sách của cha mẹ dài hơn một chút. Một số UI tạo danh sách các cha mẹ được chọn làm Idgiá trị theo thứ tự cố định. Hãy sử dụng:

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

Bây giờ cha mẹ được chọn phải được lọc từ danh sách cha mẹ theo thứ tự chính xác này.

Nếu chúng ta làm ...

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

... Thứ tự parentssẽ quyết định kết quả. Nếu cha mẹ được sắp xếp theo Id, kết quả sẽ là cha mẹ 2, 3, 4, 7. Không tốt. Tuy nhiên, chúng tôi cũng có thể sử dụng joinđể lọc danh sách. Và bằng cách sử dụng idsnhư danh sách đầu tiên, thứ tự sẽ được giữ nguyên:

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

Kết quả là bố mẹ 3, 7, 2, 4.


Vậy trong GroupJoin, các giá trị con sẽ chứa các đối tượng, có chứa các giá trị liên quan?
duyn9uyen

Như bạn đã nói GroupJoin giống như một tham gia bên ngoài nhưng cú pháp đó (hoàn toàn là linq cho tham gia nhóm) nói rằng nó không giống như tham gia bên ngoài mà là tham gia bên ngoài.
Imad

2
Tôi nghĩ rằng tôi sẽ xác định rằng "Tham gia ngoài phẳng" là Tham gia ngoài bên trái.
NetMage

1
Giải thích một cách hoàn hảo, bây giờ tôi đã hiểu
peterincumbria

19

Theo eduLINEQ :

Cách tốt nhất để hiểu rõ hơn về những gì GroupJoin làm là nghĩ đến Tham gia. Ở đó, ý tưởng tổng thể là chúng tôi đã xem qua chuỗi đầu vào "bên ngoài", tìm thấy tất cả các mục khớp từ chuỗi "bên trong" (dựa trên phép chiếu chính trên mỗi chuỗi) và sau đó thu được các cặp phần tử khớp. GroupJoin cũng tương tự, ngoại trừ việc thay vì mang lại các cặp phần tử, nó mang lại một kết quả duy nhất cho mỗi mục "bên ngoài" dựa trên mục đó và trình tự khớp các mục "bên trong" .

Sự khác biệt duy nhất là trong tuyên bố trả lại:

Tham gia :

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

Đọc thêm tại đây:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.