Esecuzione di query

Dopo essere stata creata da un utente, una query LINQ viene convertita in un albero dei comandi. Un albero dei comandi è una rappresentazione di una query compatibile con Entity Framework. L'albero dei comandi viene quindi eseguito sull'origine dati. In fase di esecuzione della query, tutte le espressioni di query, ovvero tutti i componenti della query, vengono valutate, incluse le espressioni utilizzate nella materializzazione del risultato.

Il momento di esecuzione delle espressioni di query può variare. Le query LINQ vengono sempre eseguite quando la variabile di query viene scorsa e non quando viene creata. Questo processo è noto come esecuzione posticipata. È inoltre possibile forzare l'esecuzione immediata di una query. Questa operazione è utile per memorizzare nella cache i risultati della query e verrà descritta di seguito in questo argomento.

Quando viene eseguita una query LINQ to Entities, è possibile che alcune espressioni nella query vengano eseguite nel server e che alcune parti vengano eseguite localmente nel client. La valutazione sul lato client di un'espressione viene effettuata prima dell'esecuzione della query nel server. Se un'espressione viene valutata nel client, il risultato della valutazione sostituisce l'espressione nella query e la query viene quindi eseguita nel server. Poiché le query vengono eseguite sull'origine dati, la configurazione dell'origine dati prevale sul comportamento specificato nel client. La gestione dei valori Null e la precisione numerica dipendono ad esempio dalle impostazioni del server. Tutte le eccezioni generate durante l'esecuzione della query nel server vengono passate direttamente al client.

Esecuzione posticipata

La variabile di query consente di archiviare i comandi di query solo quando la query è progettata per restituire una sequenza di valori. Se la query non contiene un metodo che causa l'esecuzione immediata, l'esecuzione effettiva della query viene posticipata fino a quando non si esegue un'iterazione dellla variabile di query in un ciclo foreach o For Each. La query viene eseguita nel server ogni volta che si scorre la variabile di query in un ciclo foreach o For Each. Questo processo è noto come esecuzione posticipata. L'esecuzione posticipata consente di combinare più query o di estendere una query. In questo caso, la query viene modificata in modo da includere nuove operazioni. Le modifiche verranno quindi riflesse nell'eventuale esecuzione. La variabile di query stessa non contiene mai i risultati della query. È quindi possibile eseguire una query il numero di volte desiderato. È ad esempio possibile disporre di un database che viene aggiornato continuamente da un'applicazione separata. Nell'applicazione è possibile creare una query che recupera i dati più recenti ed eseguirla ripetutamente a determinati intervalli, recuperando ogni volta risultati diversi.

Le query LINQ to Entities vengono convertite in alberi dei comandi in Entity Framework ed eseguite sull'origine dati quando viene eseguita l'iterazione dei risultati. In questa fase gli errori di conversione comportano la generazione di eccezioni nel client.

Esecuzione immediata

A differenza dell'esecuzione posticipata delle query che restituiscono una sequenza di valori, le query che restituiscono un valore singleton vengono eseguite immediatamente. Alcuni esempi di query singleton sono Average, Count, First e Max. Tali query vengono eseguite immediatamente perché la query deve restituire una sequenza per calcolare il risultato singleton. È anche possibile forzare l'esecuzione immediata. Questa operazione è utile quando si desidera memorizzare nella cache i risultati di una query. Per forzare l'esecuzione immediata di una query che non restituisce un valore singleton, è possibile chiamare il metodo ToList, ToDictionary o ToArray su una query o su una variabile di query. Nell'esempio seguente viene utilizzato il metodo ToArray per restituire immediatamente una matrice da una sequenza.

Using AWEntities As New AdventureWorksEntities
    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    Dim prodArray As Product() = ( _
        From product In products _
        Order By product.ListPrice Descending _
        Select product).ToArray()

    Console.WriteLine("The list price from highest to lowest:")
    For Each prod As Product In prodArray
        Console.WriteLine(prod.ListPrice)
    Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    ObjectQuery<Product> products = AWEntities.Product;

    Product[] prodArray = (
        from product in products
        orderby product.ListPrice descending
        select product).ToArray();

    Console.WriteLine("Every price from highest to lowest:");
    foreach (Product product in prodArray)
    {
        Console.WriteLine(product.ListPrice);
    }
}

È anche possibile forzare l'esecuzione inserendo il ciclo foreach o For Each immediatamente dopo l'espressione di query, mentre chiamando ToList o ToArray si memorizzano nella cache tutti i dati in un singolo oggetto Collection.

Esecuzione nell'archivio

Poiché in genere le espressioni in LINQ to Entities vengono valutate nel server, non è previsto che il comportamento dell'espressione sia conforme alla semantica CLR (Common Language Runtime), ma piuttosto a quella dell'origine dati. Vi sono tuttavia eccezioni, ad esempio quando l'espressione viene eseguita nel client. Questo può provocare risultati imprevisti, ad esempio quando il server e il client si trovano in fusi orari diversi.

Alcune espressioni nella query possono venire eseguite nel client. In generale, è previsto che la maggior parte dell'esecuzione della query avvenga nel server. Oltre ai metodi eseguiti su elementi della query mappati all'origine dati, vi sono spesso espressioni nella query che possono essere eseguite localmente. L'esecuzione locale di un'espressione di query produce un valore che può essere utilizzato nell'esecuzione della query o nella costruzione del risultato.

Determinate operazioni vengono eseguite sempre nel client, ad esempio l'associazione di valori, le sottoespressioni, le sottoquery e la materializzazione di oggetti nei risultati della query. Di conseguenza questi elementi, ad esempio i valori dei parametri, non possono essere aggiornati durante l'esecuzione. I tipi anonimi possono essere costruiti inline nell'origine dati, ma questo comportamento non deve essere presupposto. Anche i raggruppamenti inline possono essere costruiti nell'origine dati, ma questo comportamento non deve essere presupposto in ogni istanza. In generale, è consigliabile non fare presupposizioni relativamente agli elementi che verranno costruiti nel server.

In questa sezione vengono descritti scenari in cui il codice viene eseguito localmente nel client. Per ulteriori informazioni sui tipi di espressioni eseguiti localmente, vedere Espressioni nelle query LINQ to Entities.

Valori letterali e parametri

Le variabili locali, ad esempio la variabile orderID nell'esempio seguente, vengono valutate nel client.

Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

Dim orderID As Integer = 51987

Dim salesInfo = _
    From s In sales _
    Where s.SalesOrderID = orderID _
    Select s
ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;

int orderID = 51987;

IQueryable<SalesOrderHeader> salesInfo =
    from s in sales
    where s.SalesOrderID == orderID
    select s;

Anche i parametri dei metodi vengono valutati nel client. Un esempio è costituito dal parametro orderID passato al metodo MethodParameterExample, come illustrato di seguito.

Function MethodParameterExample(ByVal orderID As Integer)
    Using AWEntities As New AdventureWorksEntities()

        Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

        Dim salesInfo = _
            From s In sales _
            Where s.SalesOrderID = orderID _
            Select s

        Console.WriteLine("Sales order info:")
        For Each sale As SalesOrderHeader In salesInfo
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue)
        Next
    End Using

End Function
public static void MethodParameterExample(int orderID)
{
    using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
    {
        
        ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;
                        
        IQueryable<SalesOrderHeader> salesInfo =
            from s in sales
            where s.SalesOrderID == orderID
            select s;                

        foreach (SalesOrderHeader sale in salesInfo)
        {
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
        }
    }
}

Esecuzione del cast di valori letterali nel client

Il cast da un valore null a un tipo CLR viene eseguito nel client:

Dim contacts As ObjectQuery(Of Contact) = AWEntities.Contact

Dim query = _
    From c In contacts _
    Where c.EmailAddress = CType(Nothing, String) _
    Select c
ObjectQuery<Contact> contacts = AWEntities.Contact;

IQueryable<Contact> query =
    from c in contacts
    where c.EmailAddress == (string)null
    select c;

Il cast a un tipo, ad esempio un oggetto Decimal che ammette i valori Null, viene eseguito nel client:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
        Where product.Weight = CType(23.77, Decimal?) _
        Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == (decimal?)23.77
    select product;

Costruttori per i valori letterali

I nuovi tipi CLR che possono essere mappati ai tipi EDM vengono eseguiti nel client:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
    Where product.Weight = New Decimal(23.77) _
    Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == new decimal(23.77)
    select product;

Anche le nuove matrici vengono eseguite nel client.

Eccezioni nell'archivio

Qualsiasi errore che si verifica nell'archivio durante l'esecuzione della query viene passato al client e non viene mappato o gestito.

Configurazione dell'archivio

Quando la query viene eseguita nell'archivio, la configurazione dell'archivio sostituisce tutti i comportamenti client e la semantica dell'archivio viene espressa per tutte le operazioni e le espressioni. Ciò può determinare una differenza nel comportamento tra l'esecuzione nell'archivio e quella conforme a CLR per quanto riguarda aspetti come il confronto di valori Null, l'ordinamento di GUID, la precisione e l'accuratezza di operazioni che includono tipi di dati non precisi, ad esempio tipi a virgola mobile o DateTime, e le operazioni di stringa. È importante tenere presente questo aspetto quando si esaminano i risultati della query.

Di seguito sono ad esempio illustrate alcune differenze nel comportamento tra CLR e SQL Server:

  • In SQL Server i GUID vengono ordinati in modo diverso rispetto a CLR.

  • Possono esserci anche differenze nella precisione del risultato in caso di utilizzo del tipo Decimal in SQL Server. Queste differenze sono dovute ai requisiti fissi di precisione del tipo Decimal in SQL Server. La media dei valori Decimal 0,0, 0,0 e 1,0 è ad esempio 0,3333333333333333333333333333 nella memoria del client, ma 0,333333 nell'archivio, in base alla precisione predefinita per il tipo Decimal di SQL Server.

  • Anche alcune operazioni di confronto di stringhe vengono gestite in SQL Server in modo diverso rispetto a quanto avviene in CLR. Il comportamento del confronto di stringhe dipende dalle impostazioni delle regole di confronto nel server.

  • Le chiamate alle funzioni o ai metodi, quando incluse in una query LINQ to Entities, vengono mappate a funzioni canoniche in Entity Framework, che vengono convertite quindi in Transact-SQL ed eseguite nel database di SQL Server. In alcuni casi il comportamento delle funzioni mappate può differire dall'implementazione nelle librerie di classi di base. La chiamata ai metodi Contains, StartsWith e EndsWith con una stringa vuota come parametro restituisce ad esempio true quando eseguita in CLR, mentre restituisce false in caso di esecuzione in SQL Server. Anche il metodo EndsWith può restituire risultati diversi, in quanto due stringhe che differiscono solo per lo spazio vuoto finale vengono considerate uguali in SQL Server ma non in CLR. Questo comportamento è illustrato nell'esempio seguente:

Using AWEntities As New AdventureWorksEntities()

    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    Dim query = _
        From p In products _
        Where p.Name = "Reflector" _
        Select p.Name

    Dim q = _
        query.Select(Function(c) c.EndsWith("Reflector "))

    Console.WriteLine("LINQ to Entities returns: " & q.First())
    Console.WriteLine("CLR returns: " & "Reflector".EndsWith("Reflector "))
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{

    ObjectQuery<Product> products = AWEntities.Product;

    IQueryable<string> query = from p in products
                               where p.Name == "Reflector"
                               select p.Name;

    IEnumerable<bool> q = query.Select(c => c.EndsWith("Reflector "));

    Console.WriteLine("LINQ to Entities returns: " + q.First());
    Console.WriteLine("CLR returns: " + "Reflector".EndsWith("Reflector "));

}

Vedere anche

Altre risorse

Esecuzione di query con LINQ to Entities