Практическая разница между Linq To Entites и Linq To Objects

323
15 сентября 2017, 18:32

Изучая различия между LINQ To Entites и LINQ To Objects в EntityFramework - столкнулся с интересной вещью :

var phones = db.Phones.Where(p=> p.CompanyId == 1).ToList().Where(p=> p.Id<10);

Здесь используются два метода Where, но их реализация будет различной. В первом случае, db.Phones.Where(p=> p.CompanyId == 1) транслируется в выражение SQL, которое было рассмотрено выше. Далее метод ToList() по результатам запроса создает список в памяти компьютера. После этого мы уже имеем дело со списком в памяти, а не с базой данных. И далее вызов Where(p=> p.Id<10) будет обращаться к списку в памяти и будет представлять Linq to Object.

И мне стало интересно : А не проще ли просто получать нужное значение из БД путем простого обращения через LINQ не используя при этом LINQ To Objects?

Ведь запрос в даном случае будет выглядеть так :

var phones = db.Phones
.Where(p => p.CompanyId==1 &&
            p.TelephoneId<10)

Хотелось бы узнать какой подход в данном случае будем эффективней и почему?

Answer 1

Смотрите. Базы данных предназначены, кроме всего прочего, и для того, чтобы быстро и эффективно работать с большими объёмами данных. Например, базы данных легко справляются с выборкой из таблиц, больших по размеру, чем оперативная память. А вот получение целой такой таблицы в память с целью последующей фильтрации уже провалится по перерасходу памяти.

Также и фильтрация в базе данных может быть очень быстрой, т. к. если у неё есть индекс на фильтруемое поле, она может совместить фильтрацию с выборкой. (А особо умные базы данных могут, исходя из запросов, такой индекс и выстроить сами.) Если индекса нет, то всё равно польза от фильтрации на стороне базы в том, что выброшенные фильтром данные не нужно перегонять из базы в программу, и не нужно создавать их в основной программе только для того, чтобы тут же выбросить.

Поэтому обычно имеет смысл те части запроса, которые можно выполнить в базе данных (это та часть, при которой вы остаётесь в рамках IQueryable), выполнять на ней.

(Я не уверен, относится ли эта рекомендация к вложенным подзапросам, пускай лучше специалисты по базам данных поправят меня.)

К сожалению, скорость операций на базе данных имеет и обратную сторону: не все операции можно выполнить на базе данных, поэтому промежуточные результаты запроса приходится материализовать (то есть, получать из базы и загружать в память), используя ToList или AsEnumerable, и «дорабатывать напильником» в самой программе.

«Умения» различных баз данных отличаются между собой. Обычно база данных должна уметь фильтровать не только по равенству поля значению, но и уметь сравнивать с константой. Судя по всему, либо вам попался код для менее продвинутой базы данных (да, абстракции протекают), либо автор кода ошибся. Либо это «костыль» под какой-нибудь баг. Если сравнение возможно выполнить на базе данных, его стоит именно там и выполнять.

Answer 2

В общем случае, вы конечно правы, проще получить одним запросом к БД, чем фильтровать потом в оперативной памяти. Но тут есть несколько нюансов.

Во-первых мы должны учитывать, что в базе данных таблица несколько более сложна чем мы можем видеть в нашем коде, с ней связаны еще такие сущности как и индексы, триггеры и тд... Поэтому добавление какого либо дополнительного условия, может порождать "плохой" план запроса с полным перебором довольно больших объемов данных (например если Id входит только в составной индекс, да, для поля с таким названием это было бы странно, но... ). В таком случае, иногда, проще выбрать только небольшую часть данных, материализовать их (ToList()), и отфильтровать их уже в памяти, вместо проведения рефакторинга всей БД.

Во-вторых Phones может быть не таблицей, а представлением, и планы запросов станут в таком случае еще сложнее.

READ ALSO
Отложенное логирование

Отложенное логирование

Есть приложение, к которому приходит запрос и приложение его обрабатываетВ процессе обработки могут вылезти болячки, которые надо залогировать...

293
Как хранить объекты в List&lt;T&gt;? С#

Как хранить объекты в List<T>? С#

У меня есть абстрактный класс Izdanie, от которого наследуются классы Kniga и JournalОт Kniga наследуется класс Uchebnik

355
Поставить условие в Repeater

Поставить условие в Repeater

ЗдравствуйтеПосле обработки данных на сервере данные выводятся в Repeater, который выглядит следующим образом:

339
Премещение кнопки при наведении мыши на кнопку

Премещение кнопки при наведении мыши на кнопку

Как можно при наведении мыши на кнопку перемещать ее (так же, только еще в обратную сторону нужно)? Этот код криво справляетсяТ

293