2009-09-14 14 views
8

I metodi di estensione C# sono disponibili su IQueryable, ad es. FindNewCustomers() e FindCustomersRegisteredAfter(int year) e così via che uso per "concatenare" una query per LINQ a SQL.Problema di query compilato da LINQ a SQL (funziona come query non compilata)

Ora al mio problema: Voglio creare query compilate, per es .:

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

Il metodo FindCustomersRegisteredAfter(int year) è un metodo di estensione prendere una IQueryable e ritorno lo stesso. Il metodo OrderBy è anche un metodo di estensione (System.Linq.Dynamic) che crea un'espressione dinamica basata su una stringa (ad esempio, "FirstName ASC" ordinerà il campo FirstName in ordine crescente). Skip e Take sono i metodi integrati.

Quanto sopra (non come query compilata, ma query regolare) funziona perfect. Una volta che l'ho messo in una query compilata, mi ha colpito il seguente errore:

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

Ancora una volta, Questo funziona perfettamente se la query non è compilato, solo una query LINQ regolare. L'errore appare solo una volta all'interno di CompiledQuery.Compile().

Aiuto ??!

Edit: Se creo la query tramite interrogazione var = (...) allo stesso modo all'interno di CompiledQuery.Compile, questa è la SQL generato:

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] 

Così si vede che il SQL è tutto perfettamente traducibile, quindi devo solo riempire @ p0, @ p1 e @ p2 perché funzioni ripetutamente! Cosa c'è di sbagliato in CompiledQuery.Compile?!?

Aggiornamento: Comprendo che OrderBy non può funzionare (poiché non è un parametro @p). Sto ancora cercando di capire perché CompiledQuery.Compile non funzionerà con i miei metodi di estensione però. Le informazioni su Internet relative a questo argomento sono praticamente inesistenti.

+5

Non capisco perché questo è wiki della comunità. – JustLoren

risposta

3

Credo che la query compilata deve essere traducibile in SQL, che il metodo di estensione non può essere. Se profili il codice SQL creato dalla tua query "normale" potresti scoprire che sta selezionando l'intera tabella in modo che possa alimentare tutte le righe nel tuo metodo di estensione.

Farebbe meglio a inserire la logica di filtraggio nella query (come parte dell'albero delle espressioni) in modo che possa essere convertita in SQL ed eseguita lato server.

Anche l'ordine è un problema a causa dello Skip. Devi renderlo traducibile in SQL o LINQ dovrà restituire tutte le righe per filtrarle dal lato client.

Se non è possibile esprimerle come espressioni LINQ, prendere in considerazione la creazione di funzioni SQL sul server e il loro mapping sul DataContext. LINQ sarà in grado di tradurre quelli con le chiamate di funzione T-SQL.

EDIT:

Credo di essere stato supponendo che il metodi di estensione non stavano costruendo alberi di espressione. Scusate.

Considerate questo link che sembra simile al vostro problema.Fa riferimento ad un altro link che va più nel dettaglio.

Sembra che MethodCallExpression sia il 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.

Così, sembra che il problema è che quando la compilazione della query, il metodo non viene eseguito in modo non v'è alcun albero di espressione di tradurre a SQL.

+0

Quando creo un punto di interruzione, posso vedere che l'SQL può essere facilmente generato interamente (compresi skip, take, orderby, ecc.) Purché non sia una query compilata. – Alex

+0

Ho modificato il post per mostrare l'SQL generato. – Alex