Почему отдельный экземпляр предиката Func <T, bool> не переводится в SQL с Entity Framework?

У меня есть контекст EF Code First Db, который я использую для запроса к базе данных. Я заметил некоторые проблемы с производительностью при передаче запросов как Func<Product, bool>s из моего Aggregate Repository, и при дальнейших исследованиях выяснилось, что запросы не переводились в SQL-запросы.

Еще немного покопавшись я обнаружил следующее.

var results = _context.Products
            .Where(p => p.ProductCode.Contains("AAA"))
            .Where(p => p.CategoryId == 1)
            .ToList();

Это работает именно так, как и ожидалось . Он генерирует некоторый параметризованный SQL с var results2 = _context . Продукты . Где ( p => p . ProductCode . Содержит ( "AAA" ) && p . CategoryId == 1 ) . ToList (); Статья.

================================================== ================

Func<Product, bool> pred = (p => p.ProductCode.Contains("AAA") && p.CategoryId == 1);

var results3 = _context.Products.Where(pred).ToList();

Это также работает как ожидалось . Генерирует тот же sql как выше

================================================== ================

Expression<...>

Это сломано . Он не генерирует предложение where в SQL, он возвращает все и затем фильтрует его в коде.

sql,linq,entity-framework,ef-code-first,generic-function,

2

Ответов: 3


против 5 принят

Потому что для перевода на SQL он должен быть Func<...>, а не Where().

Это делается автоматически для вас компилятором, и поскольку перегрузки в классах Linq-to-SQL принимают Func <T, bool> s, а не делегаты, компилятор автоматически переведет ваш код (который выглядит как лямбда или анонимный метод) в объект выражения и передать его.

Однако , если вы позаботитесь о построении Expression <Func <T, bool >> самостоятельно, компилятор не сможет этого сделать, а Linq-to-SQL не использует анонимные методы, он принимает только выражения.

Что вы можете сделать, так это выполнить те части вашего запроса, которые вы можете, и затем отфильтровать результаты через вашу функцию, но я бы хотел просто изменить тип вашего значения на выражение.


Как только я опубликовал этот документ, ReSharper помог мне ответить на мой вопрос, показав мне сигнатуру метода перегрузки для Funcметода расширения.

Требуется и то Expressionи другое System.Linq.Enumerable. Если вы объявляете свои предикаты извне, вы должны использовать вариант Expression, поскольку первый не переводится в sql.


Вот почему запрос читает всю таблицу.

Когда Funcвместо a используется Expressionкомпилятор, методы выбираются System.Linq.Queryableвместо - вместо Enumerable. В Queryableметодах итерация сбора исходного (иногда лениво) в то время как Whereметоды построить дерево выражения.

Поскольку вызов to Whereне является частью дерева выражений, генератор sql не видит его во время перевода запроса.

SQL, LINQ, сущность-рамки, эф-код-первых, общая функция,
Похожие вопросы