Ottimizzare le performance di un'applicazione che utilizza Entity Framework
3 pagine in totale: <<Indietro 1 2 [3]
Ottimizzare l'ObjectTracking
Per default, l'ObjectContext mantiene una copia degli oggetti che sono stati ritrovati in memoria. Non solo, oltre a questi, l'ObjectContext conserva anche una copia dei valori originali così che si possano effettuare gli aggiornamenti delle sole colonne realmente modificate. Questo meccanismo torna molto comodo ma è anche molto dispendioso in termini sia di performance che di utilizzo di memoria.
In un esempio precedente abbiamo utilizzato il metodo Execute per scatenare l'esecuzione di una query. Come parametro abbiamo utilizzato il valore MergeOption.AppendOnly che è il valore di default. Questo parametro indica all'ObjectContext che una volta ottenuti gli oggetti dalla query, quelli che ancora non sono presenti nella cache devono essere aggiunti, mentre quelli che hanno già un equivalente nella cache vengono scartati.
Nel caso in cui non avessimo bisogno di aggiornare gli oggetti recuperati, possiamo disabilitare la possibilità di mantenere gli oggetti nell'ObjectContext passando come parametro al metodo Execute il valoreMergeOption.NoTracking. In questo modo, il processo di controllo dei dati nella cache dell'ObjectContext viene e l'eventuale inserimento in questa viene saltato ottenendo un discreto aumento di prestazioni ed una diminuzione dell'uso della memoria.
Questa opzione può essere impostata anche utilizzando i metodi ToList e ToArray semplicemente impostando la proprietà MergeOption dell'ObjectQuery<T> ottenuto dalla chiamata al metodo CreateQuery<T>. Naturalmente, questa opzione è valida sia se si utilizza Entity SQL che LINQ to Entities.
var query = ctx.CreateQuery("Vehicle").MergeOption = MergeOption.NoTracking;
var result = query.ToList();Ottimizzare le query Entity SQL con il caching del plan
La classe ObjectQuery<T> contiene una proprietà denominata EnablePlanCaching. Questa proprietà specifica se le query scritte con Entity SQL devono essere messe in una cache locale.
Attenzione, qui non si sta parlando dei risultati della query, ma della stringa che rappresenta la query. Mettendo la stringa in cache, viene mantenuta in memoria anche la versione compilata della query così che il processo di traduzione da Entity SQL a codice SQL reale sia molto più veloce.
Per default questa proprietà è impostata su true quindi il caching è abilitato. Sebbene questa caratteristica garantisca ottime performance, ci sono casi in cui si può rivelare addirittura controproducente.
Il caching è case sensitive il che significa che se scriviamo la stessa query in punti diversi utilizzando un casing diverso, la cache considera le query differenti anche se in realtà fanno la stessa cosa. Questo comporta un ovvio inutile aumento dell'utilizzo della memoria. Questo non è il solo caso in cui si rischia di penalizzare l'utilizzo della memoria. Si supponga di dover scrivere un metodo che accetta molti parametri ed in base a questi di dover creare dinamicamente una query Entity SQL. In questo caso, le possibili combinazioni tra parametri permettono di avere potenzialmente molte query ed il risultato è che si rischia di intasare la cache. Non solo, poiché ci sono molti parametri, c'è la possibilità che questi siano molto differenti ogni volta e quindi il riutilizzo delle query sia praticamente nullo.
La cache non è accessibile solo dall'ObjectQuery, ma anche dalla classe EntityCommand. Infatti, anche questa classe contiene una proprieta EnablePlanCaching che svolge esattamente il medesimo scopo di quella omonima nell'ObjectQuery.
Precompilazione delle query LINQ to Entities
Così come avviene per le query con Entity SQL e LINQ to SQL, anche le query scritte con LINQ to Entities possono essere precompilate per non dover rieseguire il parsing ogni volta che vengono utilizzate. La sintassi in questo caso è un po’ complessa. Si prenda ad esempio la seguente query.
var vehicles = from v in ctx.Vehicle where v.Name.StartsWith("C") select v;
Per precompilare la query dobbiamo prima creare una lambda expression che la rappresenti.
(DBContext ctx, string vehicleName) => from v in ctx.Vehicle where v.Name.StartsWith(vehicleName) select v;
A questo punto possiamo invocare il metodo Compile della classe System.Data.Objects.CompiledQuery e passare la lambda expression appena vista.
var compiled = CompiledQuery.Compile<DBContext, string, IQueryable<Vehicle>> ((DBContext ctx, string vehicleName) => from v in ctx.Vehicle where v.Name.StartsWith(vehicleName) select v);
Infine, basta utilizzare il metodo Invoke di CompiledQuery per lanciare la query. La prima volta la query viene compilata, mentre le altre volte viene utilizzato direttamente l'expression tree creato da questa.
var ctx = new DBContext(); IQueryable<Vehicle> vehicles = compiled.Invoke(ctx, "C").ToList();
Conclusioni
In questo articolo abbiamo introdotto le principali ottimizzazioni che possono essere effettuate in un progetto che accede ai dati utilizzando Entity Framework. Ognuna di queste ottimizzazioni va studiata ed applicata sulla base di calcoli che vanno effettuati caso per caso.
Infatti, una regola assoluta non c'è mai quando si parla di prestazioni anche se comunque le linee guida illustrate in questo articolo sono comunque da considerare abbastanza solide ed applicabili nella maggior parte dei casi.
3 pagine in totale: <<Indietro 1 2 [3]
Contenuti dell'articolo
Sullo stesso argomento
-
L'accesso ai dati nell'era del cloud computing con Windows Azure
-
Utilizzare Entity SQL per eseguire query in Entity Framework
-
Oltre il database, da Bing a Twitter: i provider per LINQ per ogni esigenza
-
Entity Framework ed NHibernate a confronto
-
Strutturare un'applicazione reale con Entity Framework
-
Utilizzare le stored procedure con Entity Framework
-
Modificare i dati con Entity Framework
-
Introduzione a LINQ to XML
-
Introduzione ad ADO.NET Entity Framework

















Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.