Проблема скомпилированного запроса LINQ to SQL (работает как некомпилированный запрос) - PullRequest
8 голосов
/ 14 сентября 2009

У меня есть методы расширения C # на IQueryable, например FindNewCustomers() и FindCustomersRegisteredAfter(int year) и т. Д., Которые я использую, чтобы «связать» вместе запрос для LINQ to SQL.

Теперь к моей проблеме: я хочу создавать скомпилированные запросы, например ::10000

</p> <pre><code> private static Func<MyDataContext, SearchInfo, IQueryable<Customer>> CQFindAll = CompiledQuery.Compile((MyDataContext dc, SearchInfo info) => dc.Contacts.Select(c => c).FindCustomersRegisteredAfter(info.RegYear) .OrderBy(info.OrderInfo) .Skip(info.SkipCount) .Take(info.PageSize));

Метод FindCustomersRegisteredAfter(int year) - это метод расширения, принимающий IQueryable и возвращающий его. Метод OrderBy также является методом расширения (System.Linq.Dynamic), который создает динамическое выражение на основе строки (например, «FirstName ASC» будет сортировать поле FirstName по возрастанию). Skip и Take являются встроенными методами.

Выше (не как скомпилированный запрос, а обычный запрос) работает perfect . Как только я поместил его в скомпилированный запрос, я обнаружил следующую ошибку:

Метод 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [Customer] (System.Linq.IQueryable`1 [Domain.Customer], Int32)' не поддерживает перевод на SQL.

Еще раз, это прекрасно работает, если запрос не скомпилирован , просто обычный запрос LINQ. Ошибка появляется только тогда, когда она находится внутри CompiledQuery.Compile ().

Помощь ??!

Редактировать: Если я создаю запрос с помощью var query = (...) так же, как и внутри CompiledQuery.Compile, это сгенерированный SQL:

</p> <pre><code>SELECT [t1].[Id], [t1].[FirstName], [t1].[LastName], [t1].[RegYear], [t1].[DeletedOn] FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [t0].[LastName]) AS [ROW_NUMBER], [t0].[Id], [t0].[FirstName], [t0].[LastName], [t0].[RegYear], [t0].[DeletedOn] FROM [dbo].[Contacts] AS [t0] WHERE ([t0].[RegYear] > @p0) AND ([t0].[DeletedOn] IS NULL) ) AS [t1] WHERE [t1].[ROW_NUMBER] BETWEEN @p1 + 1 AND @p1 + @p2 ORDER BY [t1].[ROW_NUMBER]

Итак, вы видите, что все SQL прекрасно переводимы, поэтому мне нужно только заполнить @ p0, @ p1 и @ p2, чтобы это работало многократно! Что не так с CompiledQuery.Compile?!?

Обновление: я понимаю, что OrderBy не может работать (так как это не параметр @p). Я все еще пытаюсь понять, почему CompiledQuery.Compile не будет работать с моими методами расширения. Информация в интернете на эту тему практически отсутствует.

1 Ответ

3 голосов
/ 14 сентября 2009

Я считаю, что скомпилированный запрос должен быть переведен в SQL, чего не может быть ваш метод расширения. Если вы профилируете SQL, созданный вашим «обычным» запросом, вы можете обнаружить, что он выбирает всю таблицу, чтобы он мог передать все строки в ваш метод расширения.

Вы бы лучше поместили свою логику фильтрации в запрос (как часть дерева выражений), чтобы ее можно было преобразовать в SQL и запустить на стороне сервера.

OrderBy также является проблемой из-за пропуска. Вы должны сделать это переводимым в SQL, или LINQ должен будет вернуть все строки, чтобы отфильтровать их на стороне клиента.

Если вы не можете выразить их как выражения LINQ, рассмотрите возможность создания функций SQL на сервере и сопоставления их с вашим DataContext. LINQ сможет преобразовать их в вызовы функций T-SQL.

РЕДАКТИРОВАТЬ:

Я предполагаю, что ваши методы расширения не строили деревья выражений. Сожалею.

Рассмотрим эту ссылку , которая кажется похожей на вашу проблему. Он ссылается на другую ссылку , которая более подробно описана.

Похоже, проблема в MethodCallExpression.

Код немного долго будет опубликован здесь, но похоже на tomasp.net расширитель, я посещаю каждое выражение в дерево выражений и если узел MethodCallExpression, который вызывает метод, который возвращает выражение дерево, я заменяю это MethodCallExpression по выражению дерево, возвращаемое при вызове метода.

Итак, проблема в том, что при компиляции запроса метод не выполняется, поэтому нет дерева выражений для преобразования в SQL.

...