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>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 Valuevà ChildValue. 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.