Problema di query compilato da LINQ a SQL (funziona come query non compilata)

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

Domanda

Ho metodi di estensione C # su IQueryable , ad esempio FindNewCustomers() e FindCustomersRegisteredAfter(int year) e così via che uso per "concatenare" una query per LINQ a SQL.

Ora per il mio problema: voglio creare query compilate, ad esempio:

 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 FindCustomersRegisteredAfter(int year) è un metodo di estensione che prende un IQueryable e restituisce 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 and Take sono i metodi integrati.

Quanto sopra (non come query compilata, ma query regolare) funziona perfettamente . Una volta inserito in una query compilata, ho ricevuto il seguente errore:

Metodo 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [Cliente] (System.Linq.IQueryable`1 [Domain.Customer], Int32)' non ha traduzione supportata in SQL.

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

Aiuto??!

Modifica: Se creo la query tramite var query = (...) nello stesso modo di all'interno di CompiledQuery.Compile, questo è il codice 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]

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

Aggiornamento: capisco che OrderBy non può funzionare (in quanto 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.

Risposta accettata

Credo che la query compilata deve essere traducibile in SQL, che il tuo 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.

Faresti meglio a inserire la logica di filtraggio nella query (come parte dell'albero delle espressioni) in modo che possa essere tradotta 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, valutare la possibilità di creare funzioni SQL sul server e associarle a DataContext. LINQ sarà in grado di tradurre quelli con le chiamate di funzione T-SQL.

MODIFICARE:

Suppongo che stavo assumendo che i tuoi metodi di estensione non stessero creando alberi di espressione. Scusate.

Considera questo collegamento che sembra simile al tuo problema. Fa riferimento a un altro link che va più nel dettaglio.

Sembra che MethodCallExpression sia il problema.

Il codice è un po 'lungo da pubblicare qui, ma simile all'expander di tomasp.net, visito ogni espressione nell'albero delle espressioni e se il nodo è un MethodCallExpression che chiama un metodo che restituisce un albero di espressioni, sostituisco MethodCallExpression dal albero di espressioni restituito invocando il metodo.

Quindi, sembra che il problema è che quando si compila la query, il metodo non viene eseguito, quindi non esiste una struttura di espressioni da tradurre in SQL.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché