🆕 تازههای EF Core 10: عملگرهای LeftJoin و RightJoin در LINQ
اگر تا به حال با پایگاههای داده کار کرده باشی، حتماً با LEFT JOIN (و بهصورت مشابه RIGHT JOIN) آشنا هستی. این یکی از اون چیزهاییست که تقریباً همیشه ازش استفاده میکنیم.
اما در Entity Framework Core، انجام یک Left Join همیشه کمی دردسر داشت 😅
من از joinهایی خوشم میاد که دقیقاً مثل کاری که انجام میدن خونده بشن.
متأسفانه تا قبل از این، در LINQ راه مستقیمی برای بیان Left/Right Join وجود نداشت. باید از ترکیب GroupJoin و DefaultIfEmpty استفاده میکردی، که باعث میشد کدت پیچیدهتر و سختتر برای خواندن و نگهداری بشه.
اما خبر خوب اینه که 🎉 در 10 NET. این مشکل بالاخره برطرف شده، با معرفی متدهای جدید LeftJoin و RightJoin.
❓Left Join چیه؟ (به زبان ساده)
یک LEFT JOIN تمام ردیفها از سمت چپ (Left side) و ردیفهای منطبق از سمت راست رو برمیگردونه.
اگه تطابقی وجود نداشته باشه، سمت راست مقدار null میگیره.
🔹 دلیل استفاده: برای نگه داشتن دادههای اصلی حتی زمانی که دادههای مرتبط وجود ندارن.
مثلاً نمایش تمام محصولات، حتی اگر بعضی از اونها هیچ Reviewای نداشته باشن.
🕰 روش قدیمی (GroupJoin + DefaultIfEmpty)
قبل از 10 NET. ، برای انجام یک Left Join در LINQ باید از ترکیب GroupJoin و DefaultIfEmpty استفاده میکردی تا ردیفهای سمت چپ بدون تطابق هم حفظ بشن.
این روش کار میکرد، اما مفهوم اصلی وسط کدهای اضافی گم میشد 😩
دو روش برای نوشتن این نوع query وجود داشت: Query Syntax و Method Syntax
🔹 Query Syntax
var query =
from product in dbContext.Products
join review in dbContext.Reviews on product.Id equals review.ProductId into reviewGroup
from subReview in reviewGroup.DefaultIfEmpty()
orderby product.Id, subReview.Id
select new
{
ProductId = product.Id,
product.Name,
product.Price,
ReviewId = (int?)subReview.Id ?? 0,
Rating = (int?)subReview.Rating ?? 0,
Comment = subReview.Comment ?? "N/A"
};
🧩 SQL
تولیدشده توسط EF Core برای query بالا:
SELECT
p."Id" AS "ProductId",
p."Name",
p."Price",
COALESCE(r."Id", 0) AS "ReviewId",
COALESCE(r."Rating", 0) AS "Rating",
COALESCE(r."Comment", 'N/A') AS "Comment"
FROM "Products" AS p
LEFT JOIN "Reviews" AS r ON p."Id" = r."ProductId"
ORDER BY p."Id", COALESCE(r."Id", 0)
⚙️ روش Method Syntax
var query = dbContext.Products
.GroupJoin(
dbContext.Reviews,
product => product.Id,
review => review.ProductId,
(product, reviewList) => new { product, subgroup = reviewList })
.SelectMany(
joinedSet => joinedSet.subgroup.DefaultIfEmpty(),
(joinedSet, review) => new
{
ProductId = joinedSet.product.Id,
joinedSet.product.Name,
joinedSet.product.Price,
ReviewId = (int?)review!.Id ?? 0,
Rating = (int?)review!.Rating ?? 0,
Comment = review!.Comment ?? "N/A"
})
.OrderBy(result => result.ProductId)
.ThenBy(result => result.ReviewId);
🧩 چرا این روش کار میکند:
• GroupJoin
ردیفهای دو مجموعه را بر اساس شرط تطبیق میدهد،
• DefaultIfEmpty
زمانی که هیچ تطابقی وجود ندارد، یک مقدار پیشفرض (null) را درج میکند،
در نتیجه ردیف سمت چپ همچنان حفظ میشود.
سپس با SelectMany دادهها را تخت (flatten) میکنیم.
اما بیایید صادق باشیم 😅 —
برای چیزی بهسادگی یک Left Join، این حجم از کد واقعاً زیادیه!
🚀 روش جدید در EF Core 10: استفاده از LeftJoin
الان میتونیم دقیقاً چیزی که مد نظر داریم رو بنویسیم.
LeftJoin
یک قابلیت رسمی در LINQ است و EF Core به طور خودکار آن را به LEFT JOIN در SQL ترجمه میکند.
var query = dbContext.Products
.LeftJoin(
dbContext.Reviews,
product => product.Id,
review => review.ProductId,
(product, review) => new
{
ProductId = product.Id,
product.Name,
product.Price,
ReviewId = (int?)review.Id ?? 0,
Rating = (int?)review.Rating ?? 0,
Comment = review.Comment ?? "N/A"
})
.OrderBy(x => x.ProductId)
.ThenBy(x => x.ReviewId);`
🧠 SQL تولیدشده توسط EF Core در این حالت دقیقاً مشابه مثال قبلی است.