Будет ли код оптимальным при запросе с большим количеством столбцов?
Что проверяют
Понимаете ли вы, во что обходится лишняя колонка на пути от базы до объекта в памяти, и умеете ли это проверить, а не рассуждать. Для C# следом почти всегда спрашивают про EF Core: какой SQL сгенерирует ваш LINQ и что делать с Include.
Как отвечать
Сразу честный ответ: зависит от объёма и типа колонок. Одна строка по первичному ключу с сорока полями не проблема. Тысячи таких строк или тяжёлые поля уже проблема. Цена складывается из нескольких частей:
- передача и материализация: каждая колонка идёт по сети, превращается в свойство объекта, а в EF Core ещё и попадает в change tracker;
- тяжёлые поля:
nvarchar(max),varbinary, JSON-колонки могут весить больше всей остальной строки; - покрывающие индексы: если запросу нужны три колонки, их можно отдать прямо из индекса (
INCLUDEв SQL Server и PostgreSQL). При выборке всех колонок база обязана идти в таблицу, в SQL Server это key lookup на каждую строку.
Что делать в коде:
- проекция:
Select(x => new OrderDto { x.Id, x.Total })в EF Core превращается в SELECT ровно этих колонок, и такие объекты не отслеживаются; AsNoTracking()для чтения, если нужны сущности целиком;AsSplitQuery()при несколькихIncludeна коллекции, чтобы не получить декартов взрыв строк;- в Dapper явный список колонок вместо
SELECT *.
Проверяйте, а не угадывайте: ToQueryString() или LogTo покажут SQL, план выполнения покажет lookup, BenchmarkDotNet покажет аллокации.
Где спотыкаются
Отвечают «сделаю асинхронно через ToListAsync». Асинхронность освобождает поток, но не уменьшает ни объём данных, ни время запроса.
Ставят AsEnumerable() или ToList() до Where и Select. Фильтрация и проекция уходят в память, а из базы тянется всё.
Советуют «добавить индексы на часто запрашиваемые столбцы», не связывая это с шириной выборки. Индекс по условию поиска не спасает от key lookup, если запрос требует все сорок полей.