Problema de consulta compilada de LINQ-to-SQL (funciona como una consulta no compilada)

c# compiled-query expression-trees linq linq-to-sql

Pregunta

Tengo métodos de extensión C # en IQueryable , por ejemplo, FindNewCustomers() y FindCustomersRegisteredAfter(int year) etc., que utilizo para "encadenar" una consulta para LINQ a SQL.

Ahora a mi problema: quiero crear consultas compiladas, por ejemplo:

 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));

El FindCustomersRegisteredAfter(int year) es un método de extensión que toma un IQueryable y devuelve el mismo. El método OrderBy es un método de extensión (System.Linq.Dynamic) que crea una expresión dinámica basada en una cadena (por ejemplo, "FirstName ASC" ordenará el campo FirstName ascendente). Skip y Take son los métodos incorporados.

Lo anterior (no como una consulta compilada, sino una consulta regular) funciona perfectamente . Una vez que lo puse en una consulta compilada, hice el siguiente error:

El método 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [Customer] (System.Linq.IQueryable`1 [Domain.Customer], Int32)' no tiene traducción compatible con SQL.

Una vez más, esto funciona perfectamente si la consulta no está compilada , solo una consulta LINQ regular. El error aparece solo una vez que está dentro de CompiledQuery.Compile ().

¡¿¿Ayuda??!

Editar: Si creo la consulta a través de var query = (...) de la misma manera que dentro de CompiledQuery.Compile, este es el SQL generado:

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]

¡Así que ya ves que el SQL es perfectamente traducible, así que solo tengo que rellenar @ p0, @ p1 y @ p2 para que esto funcione repetidamente! ¿Qué está mal con CompiledQuery.Compile?!?

Actualización: entiendo que OrderBy no puede funcionar (ya que no es un parámetro @p). Todavía estoy tratando de averiguar por qué CompiledQuery.Compile no funcionará con mis métodos de extensión. La información en Internet sobre este tema es prácticamente inexistente.

Respuesta aceptada

Creo que la consulta compilada debe ser traducible a SQL, que no puede ser su método de extensión. Si perfila el SQL creado por su consulta "regular", puede encontrar que está seleccionando toda la tabla para que pueda ingresar todas las filas en su método de extensión.

Sería mejor poner su lógica de filtrado en la consulta (como parte del árbol de expresiones) para que pueda ser traducido a SQL y ejecutar el lado del servidor.

El OrderBy también es un problema debido a la omisión. Debe hacer esto traducible a SQL o LINQ tendrá que devolver todas las filas para filtrarlas hacia el lado del cliente.

Si no puede expresar estas expresiones como expresiones LINQ, considere crear funciones SQL en el servidor y asignarlas a su DataContext. LINQ podrá traducirlos a las llamadas de función T-SQL.

EDITAR:

Supongo que asumí que tus métodos de extensión no estaban construyendo árboles de expresión. Lo siento.

Considera este enlace que parece similar a tu problema. Hace referencia a otro enlace que entra en más detalle.

Parece que el MethodCallExpression es el problema.

El código es un poco largo para ser publicado aquí, pero similar al expansor de tomasp.net, visito cada expresión en el árbol de expresiones y si el nodo es un MethodCallExpression que llama a un método que devuelve un árbol de expresiones, sustituyo ese MethodCallExpression por Árbol de expresiones devuelto invocando el método.

Entonces, parece que el problema es que cuando se compila la consulta, el método no se ejecuta, por lo que no hay un árbol de expresiones para traducir a SQL.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow