Condividi tramite


Considerazioni sulle prestazioni (Entity Framework)

In questo argomento vengono descritte le caratteristiche delle prestazioni del ADO.NET Entity Framework e vengono fornite alcune considerazioni per migliorare le prestazioni delle applicazioni Entity Framework.

Fasi dell'esecuzione di query

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

Operazione Costo relativo Frequenza Commenti
Caricamento dei metadati Moderato Una volta in ogni dominio applicazione. I metadati del modello e del mapping usati da Entity Framework vengono caricati in un oggetto MetadataWorkspace. Questi metadati vengono memorizzati nella cache a livello globale ed è disponibile per altre istanze di ObjectContext nello stesso dominio applicazione.
Apertura della connessione al database Moderata1 In base alle esigenze. Poiché una connessione aperta al database utilizza una risorsa preziosa, Entity Framework apre e chiude la connessione al database solo in base alle esigenze. È anche possibile aprire in modo esplicito la connessione. Per altre informazioni, vedere Gestione di connessioni e transazioni.
Generazione di visualizzazioni Alto Una volta in ogni dominio applicazione. (Può essere pregenerato. Prima che Entity Framework possa eseguire una query su un modello concettuale o salvare le modifiche all'origine dati, è necessario generare un set di viste query locali per accedere al database. A causa del costo elevato di generazione di queste visualizzazioni, è possibile pre-generare le visualizzazioni 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 Modera2 Una volta per ogni richiesta univoca. Include i costi per comporre il comando di query, generare un albero dei comandi in base ai metadati del modello e del mapping e definire la forma dei dati restituiti. Poiché ora entrambi i comandi di query Entity SQL e le query LINQ vengono memorizzati nella cache, le esecuzioni successive della stessa query richiedono meno tempo. È comunque possibile usare query LINQ compilate per ridurre questo costo nelle esecuzioni successive e le query compilate possono essere più efficienti rispetto alle query LINQ memorizzate automaticamente nella cache. Per ulteriori informazioni, consultare le 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. Non è consentita anche la parametrizzazione delle raccolte in memoria nelle query LINQ compilate.
Esecuzione della query Basso2 Una volta per ogni query. Costo dell'esecuzione del comando sull'origine dati usando il provider di dati ADO.NET. Poiché la maggior parte delle origini dati memorizza nella cache i piani di query, le esecuzioni successive della stessa query possono richiedere ancora meno tempo.
Caricamento e convalida dei tipi Basso3 Una volta per ogni ObjectContext istanza. I tipi vengono caricati e convalidati rispetto ai tipi definiti dal modello concettuale.
Tracciamento Basso3 Una volta per ogni oggetto restituito da una query. 4 Se una query usa l'opzione NoTracking di unione, questa fase non influisce sulle prestazioni.

Se la query usa l'opzione AppendOnly, PreserveChangeso OverwriteChanges merge, i risultati della query vengono rilevati in ObjectStateManager. Un EntityKey viene generato per ogni oggetto rilevato che la query restituisce e viene utilizzato per creare un ObjectStateEntry nel ObjectStateManager. Se è possibile trovare un oggetto esistente ObjectStateEntry per EntityKey, viene restituito l'oggetto esistente. Se viene utilizzata l'opzione PreserveChangeso OverwriteChanges , l'oggetto viene aggiornato prima che venga restituito.

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

1 Quando un provider di origini dati implementa il pool di connessioni, il costo di apertura di una connessione viene distribuito nel pool. Il provider .NET per SQL Server supporta il pool di connessioni.

2 I costi aumentano con maggiore complessità delle query.

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

4 Questo sovraccarico non è necessario per le query EntityClient perché le query EntityClient restituiscono un EntityDataReader invece degli oggetti. Per ulteriori informazioni, vedere Provider EntityClient per Entity Framework.

Considerazioni aggiuntive

Di seguito sono riportate altre considerazioni che possono influire sulle prestazioni delle applicazioni Entity Framework.

Esecuzione di query

Poiché le query possono essere molto impegnative in termini di risorse, è importante considerare in quale parte del codice e su quale computer viene eseguita una query.

Esecuzione posticipata e immediata

Quando si crea una query LINQ o ObjectQuery<T>, potrebbe non essere eseguita immediatamente. L'esecuzione della query viene posticipata fino a quando non sono necessari i risultati, ad esempio durante un'enumerazione foreach (C#) o For Each (Visual Basic) o quando viene assegnata per riempire una List<T> raccolta. L'esecuzione della query inizia immediatamente quando si chiama il Execute metodo su un ObjectQuery<T> oggetto o quando si chiama un metodo LINQ che restituisce una query singleton, ad esempio First o Any. Per ulteriori informazioni, vedere Query sugli oggetti e Esecuzione di query (LINQ to Entities).

Esecuzione lato client delle query LINQ

Anche se l'esecuzione di una query LINQ si verifica nel computer che ospita l'origine dati, alcune parti di una query LINQ possono essere valutate nel computer client. Per ulteriori informazioni, vedere la sezione Store Execution di Query Execution (LINQ to Entities).

Complessità di query e mapping

La complessità delle singole query e del mapping nel modello di entità avrà un effetto significativo sulle prestazioni delle query.

Complessità del mapping

I modelli più complessi di un semplice mapping uno-a-uno tra le entità nel modello concettuale e le tabelle nel modello di archiviazione generano comandi più complessi rispetto ai modelli con un mapping uno-a-uno.

Complessità delle query

Le query che richiedono un numero elevato di join nei comandi eseguiti sull'origine dati o che restituiscono una grande quantità di dati possono influire sulle prestazioni nei modi seguenti:

  • Le query su un modello concettuale che sembrano semplici possono comportare l'esecuzione di query più complesse sull'origine dati. Ciò può verificarsi perché Entity Framework converte una query su un modello concettuale in una query equivalente sull'origine dati. Quando un singolo set di entità nel modello concettuale è mappato a più tabelle nell'origine dati, o quando una relazione tra entità è mappata a una tabella di join, il comando di query eseguito sull'origine dati può richiedere uno o più join.

    Annotazioni

    Usare il metodo ToTraceString delle classi ObjectQuery<T> e EntityCommand per visualizzare i comandi eseguiti sull'origine dati per una query specifica. Per altre informazioni, vedere Procedura: Visualizzare i comandi dello Store.

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

    Di seguito è fornito un esempio di un subquery all'interno di 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 causano la generazione di una singola query con la duplicazione di oggetti tra query nidificate. Per questo motivo, una singola colonna può essere duplicata più volte. In alcuni database, incluso SQL Server, la tabella TempDB può aumentare notevolmente, riducendo così le prestazioni del server. Si consiglia di prestare attenzione quando si eseguono query annidate.

  • Qualsiasi query che restituisce una grande quantità di dati può causare una riduzione delle prestazioni se il client esegue operazioni che utilizzano risorse in modo proporzionale alle dimensioni del set di risultati. In questi casi, è consigliabile limitare la quantità di dati restituiti dalla query. Per altre informazioni, vedere Procedura: Scorrere tra i risultati delle query.

Qualsiasi comando generato automaticamente da Entity Framework può essere più complesso rispetto ai comandi simili scritti in modo esplicito da uno sviluppatore di database. Se hai bisogno di un controllo esplicito sui comandi eseguiti contro la tua origine dati, prendi in considerazione la definizione di un mapping a una funzione con valori di tabella o a una stored procedure.

Relazioni

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

Percorsi di query

Per impostazione predefinita, quando si esegue un ObjectQuery<T>, gli oggetti correlati non vengono restituiti (anche se vengono restituiti gli oggetti che rappresentano le relazioni). È possibile caricare gli oggetti correlati in uno dei tre modi seguenti:

  1. Impostare il percorso della query prima dell'esecuzione di ObjectQuery<T> .

  2. Chiamare il metodo Load sulla proprietà di navigazione che l'oggetto espone.

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

Quando si considera quale opzione usare, tenere presente che esiste un 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 dei percorsi di query

I percorsi di query definiscono il grafico degli oggetti restituiti da una query. Quando si definisce un percorso di query, è necessaria solo una singola richiesta per il database per restituire tutti gli oggetti definiti dal percorso. L'utilizzo dei percorsi di query può portare all'esecuzione di comandi complessi contro l'origine dati a partire da query di oggetti che sembrano semplici. Ciò si verifica perché uno o più join sono necessari per restituire oggetti correlati in una singola query. Questa complessità è più grande nelle query eseguite su un modello di entità complesso, come un'entità con ereditarietà o un percorso che include relazioni di tipo molti-a-molti.

Annotazioni

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

Quando un percorso di query include troppi oggetti correlati o gli oggetti contengono troppi dati di riga, l'origine dati potrebbe non essere in grado di completare la query. Ciò si verifica se la query richiede un archivio temporaneo intermedio che supera le funzionalità dell'origine dati. In questo caso, è possibile ridurre la complessità della query dell'origine dati caricando in modo esplicito gli oggetti correlati.

È possibile caricare in modo esplicito gli oggetti correlati chiamando il Load metodo su una proprietà di navigazione che restituisce un oggetto EntityCollection<TEntity> o EntityReference<TEntity>. Il caricamento esplicito degli oggetti richiede un giro di andata e ritorno al database ogni volta che Load viene chiamato.

Annotazioni

Se si chiama Load durante l'iterazione su una raccolta di oggetti restituiti, come quando si utilizza l'istruzione foreach (For Each in Visual Basic), il provider specifico dell'origine di dati deve supportare più set di risultati attivi su una singola connessione. Per un database di SQL Server, è necessario specificare un valore di MultipleActiveResultSets = true nella stringa di connessione del provider.

È inoltre possibile utilizzare il metodo LoadProperty quando non ci sono proprietà EntityCollection<TEntity> o EntityReference<TEntity> sulle entità. Ciò è utile quando si usano entità POCO.

Anche se il caricamento esplicito degli oggetti correlati riduce il numero di join e riduce la quantità di dati ridondanti, Load richiede connessioni ripetute al database, che possono diventare costose quando si carica in modo esplicito un numero elevato di oggetti.

Salva modifiche

Quando si chiama il SaveChanges metodo in un ObjectContextoggetto , viene generato un comando di creazione, aggiornamento o eliminazione separato per ogni oggetto aggiunto, aggiornato o eliminato nel contesto. Questi comandi vengono eseguiti sulla sorgente dati in un'unica transazione. Come 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 simile che non richiede DTC. L'innalzamento di livello al DTC si verificherà nelle situazioni seguenti:

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

  • Transazione esplicita con un'operazione su SQL Server 2005 quando la connessione viene gestita da Entity Framework. Ciò si verifica perché SQL Server 2005 promuove un DTC ogni volta che una connessione viene chiusa e riaperta all'interno di una singola transazione, ovvero il comportamento predefinito di Entity Framework. Questa promozione DTC non si verifica quando si usa SQL Server 2008. Per evitare questa promozione quando si usa SQL Server 2005, è necessario aprire e chiudere in modo esplicito la connessione 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 System.Transactions transazione. 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.

Preimpostare viste

La generazione di viste basate su un modello di entità è un costo significativo la prima volta che un'applicazione esegue una query. Usare l'utilità EdmGen.exe per pre-generare visualizzazioni come file di codice Visual Basic o C# che possono essere aggiunti al progetto durante la progettazione. Si può anche utilizzare il Text Template Transformation Toolkit per generare visualizzazioni precompilate. Le viste pregenerate vengono convalidate in fase di esecuzione per assicurarsi che siano coerenti 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 lavora con modelli molto grandi, si applica la seguente considerazione:

Il formato dei metadati .NET limita il numero di caratteri stringa utente in un determinato file binario a 16.777.215 (0xFFFFFF). Se si generano visualizzazioni per un modello molto grande e il file di visualizzazione raggiunge questo limite di dimensioni, si otterrà l'errore di compilazione "Nessuno spazio logico lasciato per creare più stringhe utente". Questa limitazione delle dimensioni si applica a tutti i file binari gestiti. Per altre informazioni, vedere il blog che illustra come evitare l'errore quando si lavora con modelli di grandi dimensioni e complessi.

Prendere in considerazione l'uso dell'opzione di unione NoTracking per le query

È necessario un costo per tenere traccia degli oggetti restituiti nel contesto dell'oggetto. Per rilevare le modifiche apportate agli oggetti e assicurarsi che più richieste per la stessa entità logica restituisca la stessa istanza di oggetto, gli oggetti devono essere collegati a un'istanza ObjectContext di . Se non si prevede di apportare aggiornamenti o eliminazioni agli oggetti e non è necessaria la gestione delle identità, è consigliabile usare le NoTracking opzioni di unione quando si eseguono query.

Restituisce la quantità corretta di dati

In alcuni scenari, la specifica di un percorso di query tramite il Include metodo è molto più veloce perché richiede meno round trip al database. Tuttavia, in altri scenari, i round trip aggiuntivi al database per caricare gli oggetti correlati possono essere più veloci perché le query più semplici con un minor numero di join comportano una minore ridondanza dei dati. Per questo motivo, è consigliabile testare le prestazioni di vari modi per recuperare gli oggetti correlati. Per altre informazioni, vedere Caricamento di oggetti correlati.

Per evitare di restituire troppi dati in una singola query, prendere in considerazione la suddivisione dei risultati in gruppi più gestibili. Per altre informazioni, vedere Procedura: Scorrere tra i 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). Ciò può migliorare le prestazioni assicurandosi che le risorse associate al contesto dell'oggetto vengano eliminate automaticamente quando il codice esce dal blocco di istruzioni. Tuttavia, quando i controlli sono associati a oggetti gestiti dal contesto dell'oggetto, l'istanza ObjectContext deve essere mantenuta finché l'associazione è necessaria ed eliminata manualmente. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Prendere in considerazione l'apertura manuale della connessione al database

Quando l'applicazione esegue una serie di query sugli oggetti o per eseguire chiamate frequenti di SaveChanges per rendere permanenti le operazioni di creazione, aggiornamento ed eliminazione alla fonte dei dati, Entity Framework deve continuamente aprire e chiudere la connessione alla fonte dei dati. In queste situazioni, è consigliabile aprire manualmente la connessione all'inizio di queste operazioni e chiudere o eliminare la connessione al termine delle operazioni. Per altre informazioni, vedere Gestione di connessioni e transazioni.

Dati prestazioni

Alcuni dati sulle prestazioni per Entity Framework vengono pubblicati nei post seguenti nel blog del team di ADO.NET:

Vedere anche