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

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

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

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

Это работает именно так, как и ожидалось. Он генерирует некоторый параметризованный SQL с предложением Where.

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

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

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

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

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

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

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

3 ответа

Решение

Потому что для того, чтобы перевести на SQL, он должен быть Expression<...>не Func<...>,

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

Однако, если вы позаботитесь о создании функции самостоятельно, компилятор не сможет этого сделать, а Linq-to-SQL не использует анонимные методы, он принимает только выражения.

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

Не успел я опубликовать этот ReSharper, который помог мне ответить на мой вопрос и показал подпись метода перегрузки для Where() метод расширения.

Требуется как Func<T, bool> а также Expression<Func<T, bool>>, Если вы объявляете свои предикаты извне, вы должны использовать вариант Expression, поскольку первый не переводится в sql.

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

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

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

Другие вопросы по тегам