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 Join
hai danh sách trên Id
trường, kết quả sẽ là:
Value ChildValue
A a1
A a2
A a3
B b1
B b2
Khi bạn GroupJoin
hai danh sách trên Id
trường, kết quả sẽ là:
Value ChildValues
A [a1, a2, a3]
B [b1, b2]
C []
Vì vậy, Join
tạo ra một kết quả phẳng (bảng) của các giá trị cha và con.
GroupJoin
tạ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 Join
tương đương với INNER JOIN
SQL: không có mục nào cho C
. Trong khi GroupJoin
tương đương với OUTER JOIN
: C
nằ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>
và 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 Value
và ChildValue
. Cú pháp truy vấn này sử dụng Join
phươ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 Parent
và thuộc tính loại IEnumerable<Child>
. Cú pháp truy vấn này sử dụng GroupJoin
phương thức dưới mui xe.
Chúng ta chỉ có thể thực hiện select g
trong 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ỳ join
câu lệnh nào cũng có thể được chuyển đổi thành một outer join
bằng cách thêm tương đương into g from c in g.DefaultIfEmpty()
với một join
câ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 join
trong 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 Id
giá 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ự parents
sẽ 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 ids
như 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.