Что делает LEFT JOIN в SQL
Что проверяют
Знаете ли вы поведение внешнего соединения и умеете ли выразить его в LINQ, где оно собирается не одним оператором. Ответ показывает, понимаете ли вы, какой SQL порождает ваш запрос в EF Core.
Как отвечать
Сначала SQL: в результат идут все строки левой таблицы, непарным правая часть заполняется NULL.
select e.Name, d.Title
from Employees e
left join Departments d on d.Id = e.DepartmentId;Классический способ выразить это в LINQ, пара GroupJoin и DefaultIfEmpty:
var q = from e in db.Employees
join d in db.Departments on e.DepartmentId equals d.Id into g
from d in g.DefaultIfEmpty()
select new { e.Name, Title = d == null ? null : d.Title };GroupJoin собирает группу совпадений, DefaultIfEmpty подставляет пустую, и провайдер переводит конструкцию в LEFT JOIN.
Отдельно про EF Core: обращение по необязательному навигационному свойству само превращается в левое соединение, руками его писать обычно не нужно. А Include это про загрузку графа объектов, не про форму результата, путать их не стоит.
Дальше скажите, зачем это нужно: найти сущности без пары через проверку на null, подставить ноль вместо пропуска, собрать отчёт, где все строки левой стороны обязаны присутствовать.
Где спотыкаются
Забывают про NULL на стороне значимых типов. Столбец int справа после соединения становится int?, и проекция в тип без поддержки null падает при материализации.
Второе: пишут условие по правой таблице в Where после соединения и получают внутреннее соединение: строки с null отсеиваются проверкой. Фильтр правой стороны переносится в условие соединения.
Третье: вызывают DefaultIfEmpty поверх обычного Join вместо GroupJoin. Смысл теряется, и запрос либо не переводится в SQL, либо даёт не то соединение, которое вы ожидали.