2015-08-31 19 views
12

Ho una piccola domanda sulle prestazioni con Entity Framework.Entity Framework - Prestazioni nel conteggio

Qualcosa di simile

using (MyContext context = new MyContext()) 
{ 
    Document DocObject = context.Document.Find(_id); 
    int GroupCount = context.Document.Where(w=>w.Group == DocObject.Group).ToList().Count(); 
} 

dura circa 2 secondi nel mio database (circa 30k set di dati), mentre questo

using (MyContext context = new MyContext()) 
{ 
    Document DocObject = context.Document.Find(_id); 
    int GroupCount = context.Document.Where(w=>w.Group == DocObject.Group).Count(); 
} 

richiede 0,02 secondi.

Quando il mio filtro per 10 documenti aveva 20 secondi di attesa, ho controllato il mio codice e l'ho modificato per non utilizzare ToList() prima dello Count().

Qualche idea del motivo per cui è necessaria questa linea per 2 secondi con lo ToList()?

risposta

19

Calling ToList() poi Count() volontà:

  • eseguire l'intera SELECT FROM WHERE contro il vostro database di
  • poi si materializzano tutte le entità risultanti come oggetti .NET
  • creare un nuovo List<T> oggetto contenente tutti i risultati
  • restituisce il risultato della proprietà Count dell'elenco .Net appena creato

Calling Count() contro un IQueryable volontà:

  • eseguire SELECT COUNT FROM WHERE contro il vostro database di
  • ritorno un Int32 con il numero di righe

Ovviamente, se siete interessati solo nel numero di articoli (non gli articoli stessi), quindi non si dovrebbe mai chiamare ToList() prima, in quanto richiederà un sacco di r risorse per niente.

4

Sì, ToList() valuterà i risultati (recuperando gli oggetti dal database), se non si utilizza ToList(), gli oggetti non vengono recuperati dal database.

Linq-To-Entities utilizza LazyLoading per impostazione predefinita.

Funziona in questo modo; Quando si interroga la connessione DB sottostante utilizzando Linq-To-Entities, si ottiene un oggetto proxy sul quale è possibile eseguire un numero di operazioni (il conteggio è uno). Ciò significa che non si ottengono tutti i dati dal DB contemporaneamente, ma piuttosto gli oggetti vengono recuperati dal DB al momento della valutazione. Un modo per valutare l'oggetto è utilizzare ToList().

Forse dovresti leggere this.

+1

Questa risposta non è corretta. Il problema non ha nulla a che vedere con * caricamento pigro * o esecuzione differita; entrambe le query saranno eseguite "con entusiasmo": la prima è dovuta a 'ToList()', la seconda a 'Count()'. La * differenza * è che la seconda query è * ottimizzata * per una query 'SELECT COUNT (*)' che è molto più veloce. – InBetween

+0

Questa è semantica. Non ho mai affermato che Count() non eseguisse avidamente, ma l'oggetto restituito dal metodo Where sarà del tipo IQuerable che è un oggetto proxy mentre ToList() eseguirà una selezione completa e caricherà avidamente l'intero oggetto. Count() viene eseguito in entrambi gli esempi, nel secondo viene eseguito su IQueryable e nel primo viene eseguito nell'elenco valutato . Il motivo della perdita di prestazioni è sicuramente dovuto all'esecuzione di un intero SELECT e non SELECT COUNT (*) che in questo caso è il risultato del caricamento avido degli oggetti utilizzando ToList. – Marcus

+0

Non voglio entrare in una discussione su questo. La verità è che la seconda soluzione è * più veloce * perché l'EF ottimizza la query 'DB' su una query' SELECT COUNT (*) ', un fatto che non si indica da nessuna parte nella risposta. Riesci semplicemente a caricare pigro, a interrompere l'esecuzione e ora "IQuerable " che non ha nulla a che fare con il problema. Nella seconda opzione 'Count()' * esegue anche impazientemente * la query e carica "l'oggetto (i)". La differenza è che la query è stata ottimizzata a livello di query e pertanto restituisce un singolo valore. – InBetween

1

Utilizzando il metodo di estensione Enumerable.ToList() si costruisce un nuovo oggetto Elenco dalla raccolta di origine IEnumerable<T> che significa che c'è un costo associato con fare ToList().

4

Perché ToList() sarà interrogare il database per l'intero oggetti (farà un SELECT * per così dire), e poi ti utilizzare Count() sulla lista in memoria con tutti i record, mentre se si utilizza Count() sul IQueryable (e non sul List), EF tradurrà in una semplice query SELECT COUNT(*) SQL

2

vostra prima query isnt completamente transalted a SQL - quando si chiama .ToList().Count(), si sono fondamentalmente dicendo "scaricare tutto, materializzarsi a POCO e chiamare l'interno metodo chiamato Count() "che, ovviamente, richiede un po 'di tempo.

La tua seconda query è, tuttavia, transalted a qualcosa come select count(*) from Documents where GroupId = @DocObjectGroup che è molto più veloce da eseguire e non stai materializzando nulla, solo semplice scalare.