2009-09-14 14 views
8

Tengo métodos de extensión C# en IQueryable, p. FindNewCustomers() y FindCustomersRegisteredAfter(int year) y así sucesivamente, que utilizo para "encadenar" una consulta en conjunto para LINQ to SQL.Problema de consulta compilada de LINQ-a-SQL (funciona como una consulta no compilada)

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 método FindCustomersRegisteredAfter(int year) es un método de extensión de tomar una IQueryable y devolver la misma. El método OrderBy también es un método de extensión (System.Linq.Dynamic) que crea una expresión dinámica basada en una cadena (por ejemplo, "FirstClass ASC" clasificará el campo FirstName ascendente). Skip y Take son los métodos integrados.

La consulta anterior (no tan compilada, pero consulta regular) funciona perfecta. Una vez que lo puse en una consulta compilada, me golpeó el siguiente error:

Method 'System.Linq.IQueryable`1[Domain.Customer] FindCustomersRegisteredAfter[Customer](System.Linq.IQueryable`1[Domain.Customer], Int32)' has no supported translation to SQL.

Una vez más, esto funciona perfectamente si la consulta no es compilado, sólo una consulta LINQ regular. El error aparece solo una vez dentro de CompiledQuery.Compile().

Ayuda ??

Editar: Si creo la consulta a través de var query = (...) del mismo modo que en el interior 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] 

para que veas que la SQL es perfectamente traducible, así que solo necesito completar @ p0, @ p1 y @ p2 para que esto funcione repetidamente. ¿Qué pasa con CompiledQuery.Compile?!?

Actualización: Entiendo que OrderBy no puede funcionar (ya que no es un parámetro @p). Todavía estoy intentando 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.

+5

No entiendo por qué esto es wiki de la comunidad. – JustLoren

Respuesta

3

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

Será mejor que ponga su lógica de filtrado en la consulta (como parte del árbol de expresión) para que pueda traducirse a SQL y ejecutar el lado del servidor.

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

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

EDIT:

supongo que estaba asumiendo sus métodos de extensión no estaban construyendo los árboles de expresión. Lo siento.

Considere esto link que parece ser similar a su problema.Hace referencia a otro link que entra en más detalles.

Parece que el MethodCallExpression es el problema.

The code is a bit long to be posted here, but similar to tomasp.net's expander, I visit every expression in the expression tree and if the node is a MethodCallExpression which calls a method that returns an expression tree, I replace that MethodCallExpression by the expression tree returned by invoking the method.

Por lo tanto, parece que el problema es que cuando se compila la consulta, el método no se ejecuta lo que no hay árbol de expresión para traducir a SQL.

+0

Cuando creo un punto de interrupción, puedo ver que SQL se puede generar fácilmente por completo (incluyendo omisión, captura, orden, etc.), siempre que no se trate de una consulta compilada. – Alex

+0

Edité la publicación para mostrar el SQL generado. – Alex

Cuestiones relacionadas