TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2969 2.36K
День 2468. #ЗаметкиНаПолях #ЧтоНовенького
LeftJoin и RightJoin в EF LINQ

Если вы когда-либо работали с базами данных, вы знаете о LEFT JOIN (RIGHT JOIN). Они используются довольно часто, но в Entity Framework Core выполнение левого объединения всегда было… довольно замороченным. Приходилось проделывать сложные трюки с GroupJoin и DefaultIfEmpty, что затрудняло чтение и поддержку кода. Но в .NET 10 это наконец-то исправлено с помощью новых методов LeftJoin и RightJoin.

LEFT JOIN возвращает все строки из левой части и соответствующие им строки из правой. Если совпадений нет, правая часть равна null. Это используется, чтобы сохранить «владельцев», даже если у них нет связанных строк (например, показать все товары, даже если у некоторых нет отзывов).

Старый способ
До .NET 10 для левого объединения в LINQ требовалось вызвать GroupJoin, а затем DefaultIfEmpty, чтобы оставить строки без совпадений. Это работало, но смысл был скрыт в шуме.
var query = dbContext.Products
.GroupJoin(
dbContext.Reviews,
prod => prod.Id,
review => review.ProductId,
(prod, reviews) =>
new { prod, subgroup = reviews })
.SelectMany(
j => j.subgroup.DefaultIfEmpty(),
(j, review) => new
{
ProductId = j.prod.Id,
j.prod.Name,
j.prod.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, который генерируется для этого запроса:
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)

GroupJoin сопоставляет строки, DefaultIfEmpty вставляет значение по умолчанию (NULL) при отсутствии совпадений, поэтому строка из левого множества всё равно отображается. Это слишком многословно для такого распространённого явления, как левое объединение.

Новый подход в EF 10
Теперь мы можем выразить то, что хотим:
var query = dbContext.Products
.LeftJoin(
dbContext.Reviews,
prod => prod.Id,
review => review.ProductId,
(prod, review) => new
{
ProductId = prod.Id,
prod.Name,
prod.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-код идентичен предыдущему примеру.
Здесь очевидна цель: вы видите LeftJoin, вы знаете, чего ожидать. Меньше кода, меньше изменяемых элементов (нет GroupJoin, DefaultIfEmpty, SelectMany). Тот же результат: все товары сохранены, отзывы могут отсутствовать.

RightJoin
RightJoin аналогичен - сохраняет все строки с правой стороны и только соответствующие строки с левой. EF Core преобразует это в RIGHT JOIN. Это удобно, когда необходимо сохранить вторую последовательность.

Итого
Левые объединения нужны довольно часто. Отображение всех пользователей с необязательными настройками профиля. Все товары с необязательными отзывами. Все заказы с необязательными данными о доставке. Теперь это так же просто, как и любой другой метод LINQ.

Несколько советов:
- В проекции защитите сторону, допускающую null: review.Comment ?? "N/A";
- Сохраняйте проекции небольшими, чтобы не извлекать больше столбцов, чем необходимо;
- Добавляйте индексы по ключам объединений для улучшения планов запросов.

Источник: https://www.milanjovanovic.tech/blog/whats-new-in-ef-core-10-leftjoin-and-rightjoin-operators-in-linq
  • 👍 33
More from @netdeveloperdiary
  1. Sep 30, 2026Post #3356
  2. Sep 29, 2026Фото 3 (с) Анатолий Кулаков
  3. Sep 29, 2026День 2799. Конференция DotNext 2026. Часть 1 25 и 26 сентября в Москве прошла очередная ко…
  4. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  5. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  6. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →