Condividi tramite


Considerazioni sulle prestazioni (Entity Framework)

In questo argomento vengono descritte le caratteristiche relative alle prestazioni di ADO.NET Entity Framework e vengono illustrate alcune considerazioni per migliorare le prestazioni di applicazioni Entity Framework.

Fasi di esecuzione di query

Per capire meglio le prestazioni delle query in Entity Framework, è utile capire le operazioni che si verificano quando una query viene eseguita su un modello concettuale e restituisce dati come oggetti. Nella tabella seguente viene descritta questa serie di operazioni.

Operazione Costo relativo Frequenza Commenti
Caricamento di metadati Moderato Una volta in ogni dominio dell'applicazione. I metadati del modello e di mapping usati da Entity Framework sono caricati in un MetadataWorkspace. Questi metadati sono memorizzati nella cache globalmente e sono disponibili per altre istanze di ObjectContext nello stesso dominio dell'applicazione.
Apertura della connessione al database Moderato1 Secondo le necessità. Poiché una connessione aperta al database utilizza una risorsa preziosa, Entity Framework apre e chiude la connessione di database solo in base alle esigenze. È possibile aprire anche in modo esplicito la connessione. Per altre informazioni, vedere Gestione di connessioni e transazioni.
Generazione di visualizzazioni Alta Una volta in ogni dominio dell'applicazione. Possono essere generate anticipatamente. Prima di poter eseguire una query su un modello concettuale o salvare delle modifiche all'origine dati, Entity Framework deve generare un set di visualizzazioni query locali per accedere al database. A causa del costo elevato della generazione di queste visualizzazioni, è possibile generarle in anticipo e aggiungerle al progetto in fase di progettazione. Per altre informazioni, vedere Procedura: Pre-generare viste per migliorare le prestazioni delle query.
Preparazione della query Moderato2 Una volta per ogni query univoca. Include i costi per creare il comando della query, generare un albero dei comandi basato sui metadati del modello e di mapping e definire la forma dei dati restituiti. Poiché vengono memorizzati nella cache sia i comandi delle query Entity SQL sia le query LINQ, le successive esecuzioni dei comandi della stessa query sono più veloci. Tuttavia, è possibile usare le query LINQ compilate per ridurre il costo nelle esecuzioni successive e le query compilate possono essere più efficienti di quelle LINQ che vengono memorizzate nella cache automaticamente. Per altre informazioni, vedere Query compilate (LINQ to Entities). Per informazioni generali sull'esecuzione di query LINQ, vedere LINQ to Entities. Nota: le query LINQ to Entities che applicano l'operatore Enumerable.Contains alle raccolte in memoria non vengono memorizzate automaticamente nella cache. Inoltre, la parametrizzazione delle raccolte in memoria nelle query LINQ compilate non è consentita.
Esecuzione della query Basso2 Una volta per ogni query. Costo dell'esecuzione del comando sull'origine dati tramite il provider di dati ADO.NET. Poiché la maggior parte delle origini dati memorizzano nella cache i piani di query, è possibile che le successive esecuzioni della stessa query siano ancor più veloci.
Caricamento e convalida di tipi Basso3 Una volta per ciascuna istanza ObjectContext. I tipi vengono caricati e convalidati rispetto ai tipi definiti nel modello concettuale.
Rilevamento Basso3 Una volta per ogni oggetto restituito da una query. 4 Se una query usa l'opzione di merge NoTracking, questa fase non influisce sulle prestazioni.

Se la query usa l'opzione di merge AppendOnly, PreserveChanges o OverwriteChanges, i risultati della query vengono rilevati nell'oggetto ObjectStateManager. Un oggetto EntityKey viene generato per ogni oggetto rilevato che la query restituisce e viene usato per creare un oggetto ObjectStateEntry in ObjectStateManager. Se è possibile trovare un oggetto ObjectStateEntry per EntityKey, viene restituito l'oggetto esistente. Se viene usata l'opzione PreserveChanges o OverwriteChanges, l'oggetto viene aggiornato prima di essere restituito.

Per altre informazioni, vedere Risoluzione delle identità, Gestione stato e Rilevamento modifiche.
Materializzazione degli oggetti Moderato3 Una volta per ogni oggetto restituito da una query. 4 Processo di lettura dell'oggetto DbDataReader restituito, di creazione di oggetti e di impostazione di valori di proprietà che si basano sui valori in ciascuna istanza della classe DbDataRecord. Se l'oggetto esiste già in ObjectContext e la query usa l'opzione di unione AppendOnly o PreserveChanges, questa fase non influisce sulle prestazioni. Per altre informazioni, vedere Risoluzione delle identità, Gestione stato e Rilevamento modifiche.

1 Quando un provider dell'origine dati implementa pool di connessioni, il costo di apertura di una connessione è distribuito nel pool. Il provider .NET per SQL Server supporta i pool di connessioni.

2 Il costo aumenta con la maggiore complessità della query.

3 Il costo totale aumenta proporzionalmente al numero di oggetti restituiti dalla query.

4 Questo overhead non è richiesto per le query EntityClient perché le query EntityClient restituiscono un elemento EntityDataReader anziché oggetti. Per ulteriori informazioni, vedere Provider EntityClient per Entity Framework.

Considerazioni aggiuntive

Di seguito sono illustrate altre considerazioni che possono influire sulle prestazioni di applicazioni Entity Framework.

Esecuzione di query

Poiché le query possono richiedere l'uso intenso delle risorse, è bene considerare in quale punto del codice e in quale computer viene eseguita una query.

Esecuzione posticipata e immediata

Quando si crea un oggetto ObjectQuery<T> o una query LINQ, è possibile che la query non sia eseguita immediatamente. L'esecuzione della query è rinviata fino a quando i risultati non diventano necessari, ad esempio durante un'enumerazione foreach (C#) o For Each (Visual Basic) o quando è assegnata per completare una raccolta List<T>. L'esecuzione della query inizia immediatamente quando si chiama il metodo Execute in un oggetto ObjectQuery<T> o quando si chiama un metodo LINQ che restituisce una query Singleton, ad esempio First o Any. Per altre informazioni, vedere Query sugli oggetti ed Esecuzione di query (LINQ to Entities).

Esecuzione di query LINQ sul lato client

Sebbene l'esecuzione di una query LINQ avvenga nel computer che ospita l'origine dati, è possibile che alcune parti della query LINQ vengano valutate nel computer client. Per altre informazioni, vedere la sezione Esecuzione nell'archivio di Esecuzione di query (LINQ to Entities).

Complessità delle query e del mapping

La complessità di query singole e del mapping nel modello dell'entità influirà in modo significativo sulle prestazioni delle query.

Complessità del mapping

I modelli che sono più complessi di un mapping uno a uno tra entità nel modello concettuale e tabelle nel modello di archiviazione generano comandi più complessi rispetto ai modelli che dispongono di un mapping uno a uno.

Complessità delle query

Le query con un elevato numero di join nei comandi che sono eseguite sull'origine dati o che restituiscono una grande quantità di dati influiscono sulle prestazioni nei seguenti modi:

  • Le query su un modello concettuale che sembrano semplici possono comportare l'esecuzione di query più complesse sull'origine dati. Il motivo consiste nel fatto che Entity Framework traduce una query su un modello concettuale in una equivalente query sull'origine dati. Quando un singolo set di entità nel modello concettuale esegue il mapping a più tabelle nell'origine dati, o quando una relazione tra entità viene mappata a una tabella di join, è possibile che il comando di query eseguito sulla query dell'origine dati richieda uno o più join.

    Nota

    Usare il metodo ToTraceString della classe ObjectQuery<T> o EntityCommand per visualizzare i comandi che sono eseguiti sull'origine dati per una data query. Per altre informazioni, vedere Procedura: Visualizzare i comandi dell’archivio.

  • Le query Entity SQL annidate possono creare join nel server e possono restituire un elevato numero di righe.

    Di seguito è riportato un esempio di query annidata in una clausola di proiezione:

    SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c  ) As Inner2
        FROM AdventureWorksModel.JobCandidate AS c  ) As Inner1
        FROM AdventureWorksModel.EmployeeDepartmentHistory AS c  
    

    Inoltre, tali query fanno in modo che la pipeline delle query generi un'unica query con duplicazione di oggetti tra le query annidate. Per questo motivo una singola colonna può essere duplicata più volte. In alcuni database, tra cui SQL Server, questo può causare un forte aumento delle dimensioni della tabella TempDB, con effetti negativi sulle prestazioni del server. Occorre quindi prestare attenzione quando si eseguono query annidate.

  • Qualsiasi query che restituisce un elevato numero di dati può causare una diminuzione delle prestazioni se il client in quel momento sta eseguendo operazioni che consumano risorse in modo proporzionale alle dimensioni del set dei risultati. In tali casi, è necessario limitare la quantità di dati restituiti dalla query. Per altre informazioni, vedere Procedura: Pagina dei risultati delle query.

Qualsiasi comando generato automaticamente da Entity Framework può essere più complesso degli analoghi comandi scritti in modo esplicito da un sviluppatore del database. Se è necessario avere il controllo esplicito sui comandi eseguiti sull'origine dati, si può definire un mapping a una funzione con valori di tabella o una stored procedure.

Relazioni

Per ottenere prestazioni ottimali nell'esecuzione delle query, è necessario definire delle relazioni tra entità sia come associazioni nel modello di entità che come relazioni logiche nell'origine dati.

Percorsi della query

Per impostazione predefinita, quando si esegue un ObjectQuery<T>, gli oggetti correlati non vengono restituiti (anche se si tratta di oggetti che rappresentano le relazioni). È possibile caricare oggetti correlati in una delle tre modalità riportate di seguito:

  1. Impostando il percorso della query prima che venga eseguito l'oggetto ObjectQuery<T>.

  2. Chiamando il metodo Load sulla proprietà di navigazione esposta dall'oggetto.

  3. Impostando l'opzione LazyLoadingEnabled nell'oggetto ObjectContext su true. Si noti che questa operazione viene eseguita automaticamente quando si genera codice a livello di oggetto con Finestra di progettazione Entity Data Model. Per altre informazioni, vedere Panoramica del codice generato.

Nella scelta dell'opzione da usare, considerare il compromesso tra il numero di richieste nel database e la quantità di dati restituiti in una singola query. Per altre informazioni, vedere Caricamento di oggetti correlati.

Uso di percorsi della query

I percorsi della query definiscono il grafico degli oggetti restituiti da una query. Quando si definisce un percorso della query, è sufficiente una sola richiesta al database per restituire tutti gli oggetti definiti dal percorso. L'uso di percorsi della query può comportare l'esecuzione di comandi complessi nell'origine dati, derivanti da query di oggetto apparentemente semplici. Questo si verifica in quanto per restituire oggetti correlati in una singola query sono necessari uno o più join. Questa complessità è maggiore nelle query su un modello di entità complesso, ad esempio un'entità con ereditarietà o un percorso che include relazioni molti-a-molti.

Nota

Usare il metodo ToTraceString per visualizzare il comando che verrà generato da un oggetto ObjectQuery<T>. Per altre informazioni, vedere Procedura: Visualizzare i comandi dell’archivio.

Quando un percorso della query include troppi oggetti correlati o gli oggetti contengono troppi dati delle righe, potrebbe non essere possibile completare la query dall'origine dati. Questo si verifica se la query richiede un'archiviazione temporanea intermedia superiore alle possibilità dell'origine dati. In questo caso, è possibile ridurre la complessità della query sull'origine dati caricando in modo esplicito gli oggetti correlati.

È possibile caricare in modo esplicito degli oggetti correlati chiamando il metodo Load in una proprietà di navigazione che restituisce un oggetto EntityCollection<TEntity> o EntityReference<TEntity>. Il caricamento esplicito di oggetti richiede un round trip al database ad ogni chiamata del metodo Load.

Nota

Se si chiama il metodo Load durante l'esecuzione di ciclo in una raccolta di oggetti restituiti, come quando si usa l'istruzione foreach (For Each in Visual Basic), è necessario che il provider specifico dell'origine dati supporti più set dei risultati attivi in un'unica connessione. Per un database di SQL Server, è necessario specificare un valore di MultipleActiveResultSets = true nella stringa di connessione del provider.

È inoltre possibile usare il metodo LoadProperty quando non si dispone di proprietà EntityCollection<TEntity> o EntityReference<TEntity> sulle entità. Questa situazione si rivela utile quando si usano entità POCO.

Anche se il caricamento esplicito di oggetti correlati ridurrà il numero di join e ridurrà la quantità di dati ridondanti, Load richiede ripetute connessioni al database e questa procedura può diventare costosa se si caricano in modo esplicito molti oggetti.

Salvataggio delle modifiche

Quando si chiama il metodo SaveChanges in un oggetto ObjectContext, viene generato un comando di creazione, aggiornamento o eliminazione distinto per ogni oggetto aggiunto, aggiornato o eliminato nel contesto. Questi comandi vengono eseguiti sull'origine dati in una sola transazione. Come avviene per le query, le prestazioni delle operazioni di creazione, aggiornamento ed eliminazione dipendono dalla complessità del mapping nel modello concettuale.

Transazioni distribuite

Le operazioni in una transazione esplicita che richiedono risorse gestite dal DTC (Distributed Transaction Coordinator) saranno molto più costose di un'operazione analoga che non richiede il DTC. Una promozione al DTC avverrà nelle situazioni seguenti:

  • Una transazione esplicita con un'operazione su un database SQL Server 2000 o altra origine dati che promuove sempre transazioni esplicite al DTC.

  • Una transazione esplicita con un'operazione relativa a SQL Server 2005 quando la connessione è gestita da Entity Framework. Ciò si verifica perché SQL Server 2005 promuove a un DTC ogni qualvolta una connessione viene chiusa e riaperta all'interno di una singola transazione, che è il comportamento predefinito di Entity Framework. Questa promozione al DTC non avviene quando si utilizza SQL Server 2008. Per evitare questa promozione quando si utilizza SQL Server 2005, è necessario aprire e chiudere la connessione in modo esplicito all'interno della transazione. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Una transazione esplicita viene usata quando una o più operazioni vengono eseguite all'interno di una transazione System.Transactions. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Strategie per migliorare le prestazioni

È possibile migliorare le prestazioni complessive delle query in Entity Framework usando le strategie seguenti.

Generare in anticipo le visualizzazioni

La generazione di visualizzazioni basate su un modello di entità ha un costo significativo la prima volta che un'applicazione esegue una query. Usare EdmGen.exe per generare in anticipo visualizzazioni come un file di codice Visual Basic o C# che può essere aggiunto al progetto nella fase di progettazione. Per generare visualizzazioni pre-compilate, è inoltre possibile usare il toolkit di trasformazione dei modelli di testo. Le visualizzazioni generate in anticipo vengono convalidate in fase di esecuzione per garantirne la coerenza con la versione corrente del modello di entità specificato. Per altre informazioni, vedere Procedura: Pre-generare viste per migliorare le prestazioni delle query.

Quando si usano modelli di dimensioni elevate, si applica la considerazione seguente:

Il formato dei metadati .NET limita il numero di caratteri di stringa specificabili dall'utente in un file binario a 16.777.215 (0xFFFFFF). Se si generano visualizzazioni per un modello di dimensioni elevate e il file relativo raggiunge questo limite, verrà visualizzato l'errore di compilazione "Nessuno spazio logico lasciato per creare altre stringhe utente". Questa limitazione delle dimensioni si applica a tutti i file binari gestiti. Per ulteriori informazioni, vedere il blog che descrive come evitare l'errore quando si utilizzano modelli complessi e di dimensioni elevate.

Usare l'opzione di merge NoTracking per le query

Esiste un costo necessario per tenere traccia degli oggetti restituiti nel contesto dell'oggetto. Il rilevamento di modifiche agli oggetti e la garanzia che più richieste per la stessa l'entità logica restituiscano la stessa istanza dell'oggetto richiedono che gli oggetti siano allegati a un'istanza ObjectContext. Se non si intende effettuare aggiornamenti o eliminazioni di oggetti e non si richiede la gestione di identità, può essere utile usare le opzioni di merge NoTracking per l'esecuzione di query.

Restituire la quantità corretta di dati

In alcuni scenari, la specifica di un percorso della query usando il metodo Include è molto più veloce perché richiede meno round trip al database. Tuttavia, in altri scenari, l'esecuzione di round trip aggiuntivi al database per caricare oggetti correlati potrebbe risultare più veloce perché le query più semplici con meno join comportano meno ridondanza di dati. Per questo motivo si consiglia di testare le prestazioni usando varie modalità di recupero di oggetti correlati. Per altre informazioni, vedere Caricamento di oggetti correlati.

Per evitare la restituzione di troppi dati in una sola query, si può considerare di eseguire il paging dei risultati della query in gruppi più gestibili. Per altre informazioni, vedere Procedura: Pagina dei risultati delle query.

Limitare l'ambito di ObjectContext

Nella maggior parte dei casi, è necessario creare un'istanza ObjectContext all'interno di un'istruzione using (Using…End Using in Visual Basic). In questo modo le prestazioni migliorano in quanto viene garantita l'eliminazione automatica delle risorse associate al contesto dell'oggetto quando il codice esce dal blocco di istruzioni. Tuttavia, quando i controlli vengono associati a oggetti gestiti dal contesto dell'oggetto, l'istanza ObjectContext deve essere gestita finché l'associazione è necessaria e viene eliminata manualmente. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Aprire manualmente la connessione al database

Quando l'applicazione esegue una serie di query sugli oggetti o chiamate SaveChanges frequenti per rendere persistenti le operazioni di creazione, aggiornamento ed eliminazione nell'origine dati, Entity Framework deve aprire e chiudere continuamente la connessione all'origine dati. In queste situazioni, può essere utile aprire manualmente la connessione all'inizio di queste operazioni e chiuderla o eliminarla quando le operazioni sono completate. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Dati prestazioni

Alcuni dati relativi alle prestazioni di Entity Framework sono pubblicati nei post seguenti nel blog del team di ADO.NET:

Vedi anche