EF Core 10 đã gọn hơn với LeftJoin và RightJoin trong LINQ
EF Core 10 bổ sung chính thức các toán tử LeftJoin và RightJoin, giúp câu lệnh LINQ dễ đọc, rõ ý định và dễ bảo trì hơn.
Nếu bạn đã làm việc với cơ sở dữ liệu quan hệ đủ lâu, chắc bạn hiểu LEFT JOIN và RIGHT JOIN quan trọng đến mức nào.
Nhưng nếu bạn từng dùng Entity Framework Core trước .NET 10, bạn cũng sẽ nhớ cảm giác "khổ sở" ấy 😅
Viết một câu left join đơn giản trong LINQ trước đây thường rườm rà không cần thiết, phải đi qua GroupJoin, DefaultIfEmpty, rồi SelectMany. Nó vẫn chạy đúng, nhưng ý định của đoạn code lại không hề rõ ràng.
Tin vui là EF Core 10 cuối cùng đã bổ sung LeftJoin và RightJoin - giúp code gần hơn nhiều với cách chúng ta tư duy.
LEFT JOIN là gì?
Một LEFT JOIN sẽ trả về:
- Tất cả bản ghi ở bên trái
- Các bản ghi khớp ở bên phải
- Nếu không có dữ liệu khớp thì giá trị bên phải sẽ là
null
Ví dụ đơn giản
Giả sử bạn có:
CustomersOrders
Bạn muốn hiển thị toàn bộ khách hàng, kể cả những người chưa từng đặt đơn hàng nào.
Cách cũ trước .NET 10
Để thực hiện left join, trước đây bạn thường phải viết như sau.
Query syntax
var query =
from customer in dbContext.Customers
join order in dbContext.Orders
on customer.Id equals order.CustomerId into orderGroup
from subOrder in orderGroup.DefaultIfEmpty()
select new
{
CustomerId = customer.Id,
customer.Name,
OrderId = (int?)subOrder.Id ?? 0,
Total = (decimal?)subOrder.Total ?? 0
};
Method syntax
var query = dbContext.Customers
.GroupJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, orders) => new { customer, orders })
.SelectMany(
x => x.orders.DefaultIfEmpty(),
(x, order) => new
{
CustomerId = x.customer.Id,
x.customer.Name,
OrderId = (int?)order!.Id ?? 0,
Total = (decimal?)order!.Total ?? 0
});
Vì sao cách này gây khó chịu?
- Quá dài cho một thao tác rất phổ biến
- Khó đọc vì ý định thật sự bị che khuất
- Dễ viết sai
- Không thân thiện với người mới
Cách mới với LeftJoin trong EF Core 10
Giờ đây chúng ta có thể viết đúng thứ mình muốn thể hiện:
var query = dbContext.Customers
.LeftJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, order) => new
{
CustomerId = customer.Id,
customer.Name,
OrderId = (int?)order.Id ?? 0,
Total = (decimal?)order.Total ?? 0
});
Vì sao đây là thay đổi đáng giá?
- Code ngắn gọn và sạch hơn nhiều
- Ý định thể hiện ngay lập tức
- Không còn phải ghép
GroupJoin + DefaultIfEmpty - SQL sinh ra phía dưới vẫn tương đương
Còn RIGHT JOIN thì sao?
EF Core 10 cũng bổ sung RightJoin.
Nó làm điều ngược lại:
- Giữ tất cả bản ghi ở bên phải
- Ghép dữ liệu từ bên trái nếu có
Ví dụ
Giả sử bạn muốn hiển thị toàn bộ đơn hàng, kể cả khi một vài khách hàng đã bị xóa hoặc không còn tồn tại trong dữ liệu:
var query = dbContext.Customers
.RightJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, order) => new
{
CustomerName = customer.Name,
OrderId = order.Id,
order.Total
});
Lưu ý quan trọng
- Hiện tại, query syntax kiểu
from ... selectchưa hỗ trợLeftJoinvàRightJoin - Bạn cần dùng method syntax
Khi nào bạn sẽ dùng tính năng này?
Thực tế là gần như ở khắp nơi:
- Khách hàng có thể có hoặc không có đơn hàng
- Người dùng có hoặc không có hồ sơ chi tiết
- Sản phẩm có hoặc không có giảm giá
- Nhân viên có hoặc không có phòng ban
Trước đây, nhiều người thường:
- Viết nhiều query tách rời
- Vô tình dùng
INNER JOINvà làm mất dữ liệu
Giờ thì lý do để né LEFT JOIN gần như không còn nữa.
Các thực hành tốt
- Luôn xử lý
nullở phía dữ liệu tùy chọn
order.Total ?? 0
- Giữ projection gọn, chỉ lấy những cột cần thiết
- Thêm index cho join key để tối ưu hiệu năng
Kết luận
Đây là một trong những tính năng nhỏ nhưng tạo khác biệt lớn trong trải nghiệm code hằng ngày.
Với LeftJoin và RightJoin:
- Code sạch hơn
- Ý định rõ ràng hơn
- Ít khả năng sinh bug hơn
Cuối cùng thì LINQ cũng đã tiến gần hơn tới SQL, nhưng không còn mang theo cảm giác đau đầu như trước.
Support My Work
If you've enjoyed my content and found it helpful, consider buying me a coffee. It keeps me caffeinated and creating!