Что происходит с навигационными свойствами в EF Core при проекции в анонимный тип?
Что проверяют
Понимаете ли вы, что проекция меняет весь план запроса, а не только форму результата. Заодно смотрят, читаете ли вы предупреждения в логе: ответ «надо добавить Include» выдаёт человека, который подбирает вызовы наугад.
Как отвечать
Ключевое: в проекции навигационные свойства транслируются в SQL. Обращение к o.Customer.Name внутри Select превращается в соединение таблиц, отдельное указание загрузки не нужно.
var rows = await db.Orders
.Where(o => o.IsPaid)
.Select(o => new { o.Id, Customer = o.Customer.Name })
.ToListAsync();Что назвать дальше:
Includeперед проекцией в анонимный тип игнорируется, провайдер пишет об этом предупреждение в лог. Читаются ровно те колонки, что перечислены в проекции;- ленивая загрузка не участвует: анонимный тип не сущность, прокси для него не создаётся, после материализации догружать нечего;
- отслеживание: набор скалярных полей в трекер не попадает, а целая сущность, положенная в проекцию, отслеживается как обычно;
- коллекционная навигация тоже переводится:
Items = o.Items.Select(i => i.Sku).ToList()выполнится запросом, а не обходом в памяти; - необязательная связь даёт левое соединение, и в поле приезжает null.
Заканчивайте выводом: проекция это способ не тащить лишние колонки, поэтому список и карточка обычно читаются разными запросами.
Где спотыкаются
Добавляют загрузку связей и ждут, что она подействует на анонимный тип. Связь и так подтянется, если упомянута в проекции, а если не упомянута, её не будет ни с какими указаниями.
Переносят привычки к сущностям на проекцию. Поле Customer.Name у заказа без клиента вернёт null на стороне базы, но то же выражение, вычисленное уже в памяти после перехода к перечислению, бросит исключение обращения по пустой ссылке.
Кладут в проекцию свой метод и не знают правил. В финальной проекции такой вызов разрешён и выполняется в памяти для каждой строки, а тот же вызов в фильтре или во вложенной проекции уронит запрос ошибкой о непереводимом выражении.