2010-06-16 17 views
6

Ho una query di linq abbastanza complessa per le entità che visualizzo su un sito web. Usa il paging quindi non faccio mai cadere più di 50 record alla volta per la visualizzazione.Come gestire set di risultati di grandi dimensioni con Linq alle entità?

Ma voglio anche dare all'utente la possibilità di esportare i risultati completi in Excel o in un altro formato di file.

La mia preoccupazione è che potrebbe esserci potenzialmente un gran numero di record caricati tutti in memoria contemporaneamente per fare ciò.

C'è un modo per elaborare un set di risultati linq 1 record alla volta come si potrebbe w/a datareader in modo che solo 1 record venga effettivamente tenuto in memoria alla volta?

Ho visto suggerimenti che se si enumera sulla query linq con un ciclo foreach che i record non saranno tutti letti in memoria in una sola volta e non sovraccaricheranno il server.

Qualcuno ha un collegamento a qualcosa che potrei leggere per verificarlo?

Apprezzerei qualsiasi aiuto.

Grazie

+2

il risultato non dovrebbe essere una raccolta in memoria per impostazione predefinita. Stai facendo qualcosa (ToArray, ToList, qualsiasi cosa) per far sì che tutto venga portato in memoria? In caso contrario, basta scorrere i risultati (foreach, Select, qualunque) e si dovrebbe andare bene. –

+1

Attualmente il mio metodo di ricerca chiama ToList() e restituisce l'elenco, ma è possibile cambiarlo facilmente per restituire invece QueryObject. Sei sicuro che se eseguirò un ciclo foreach su IQueryable, trasmetterà i dati in streaming? – user169867

+0

Quindi il tuo 'ToList()' fondamentalmente farà qualcosa di simile a 'Lista list = new List (); foreach (oggetto Object in query) {list.Add (oggetto); } '. Il 'foreach' chiamerà' query.MoveNext() '. Quindi stai caricando tutto nella lista e aumentando l'utilizzo della memoria. (Ho saltato il pezzo GetEnumerator(), ecc., Quindi non è accurato) –

risposta

5

set the ObjectContext to MergeOption.NoTracking (poiché è una sola operazione di lettura). Se si sta utilizzando lo stesso ObjectContext per il salvataggio di altri dati, Detach the object dal contesto.

come staccare

foreach(IQueryable) 
{ 
    //do something 
    objectContext.Detach(object); 
} 

Edit: Se si utilizza NoTracking opzione, non v'è alcuna necessità di staccare

Edit2: Ho scritto a Matt Warren su questo scenario. E metto rilevanti corrispondenze private qui, con la sua approvazione

I risultati di SQL Server potrebbero non persino essere tutte prodotte dal server ancora. La query è stata avviata sul server e il primo lotto di risultati viene trasferito al client, ma non vengono prodotti altri (o sono memorizzati nella cache sul server) fino a quando il client non ha ricevuto le richieste del client . Questa è la cosiddetta modalità 'firehose cursor' o talvolta denominata come streaming. Il server invia loro il più velocemente che può, e il client sta leggendo loro il più velocemente si può (il codice), ma c'è un protocollo di dati trasferimento sotto quel richiede il riconoscimento da parte del cliente di continuare l'invio di più dati

Dal IQueryable eredita da IEnumerable, credo che la query sottostante inviata al server sarebbe stato lo stesso. Tuttavia, quando facciamo un IEnumerable.ToList(), il lettore di dati, che viene utilizzato dalla connessione sottostante, inizia a popolare l'oggetto, gli oggetti vengono caricati nel dominio dell'app e potrebbero esaurire la memoria che questi oggetti non possono ancora essere smaltiti.

Quando si utilizza foreach e IEunmerable il lettore di dati legge il set di risultati SQL uno alla volta, gli oggetti vengono creati e quindi eliminati. La connessione sottostante potrebbe ricevere dati in blocchi e potrebbe non inviare una risposta a SQL Server fino a quando non vengono letti tutti i blocchi. Quindi non verrà eseguito in 'di eccezione memory`

Edit3:

Quando la query è in esecuzione, in realtà si può aprire lo SQL Server 'Monitoraggio Attività' e vedere la query, lo Stato un'attività come SUSPENDED e Wait Type come Async_network_IO - che in realtà indica che il risultato è nel buffer di rete di SQL Server. Puoi leggere ulteriori informazioni a riguardo here e here

+0

Sì, anche l'impostazione NoTracking on dovrebbe essere d'aiuto. Sai se esiste un modo per impostarlo sull'intero contesto? L'ho appena impostato su ObjectSet all'interno del contenuto. – user169867

+0

stai cercando 'objectContext.MergeOption = MergeOption.NoTracking'? http://msdn.microsoft.com/en-us/library/bb738896.aspx – ram

+0

Non penso che ObjectContext abbia una proprietà MergeOption. Stai pensando ad ObjectQuery invece? – user169867

1

Controllare il valore restituito della query LINQ. Dovrebbe essere IEnumerable<>, che carica solo un oggetto alla volta. Se poi usi qualcosa come .ToList(), saranno tutti caricati in memoria. Assicurati che il tuo codice non mantenga una lista o usi più di un'istanza alla volta e starai bene.

Edit: Per aggiungere a ciò che le persone hanno detto su foreach ... Se si fa qualcosa di simile:

var query = from o in Objects 
      where o.Name = "abc" 
      select o; 

foreach (Object o in query) 
{ 
    // Do something with o 
} 

La porzione di query utilizza esecuzione differita (see examples), quindi gli oggetti non sono in memoria ancora. Il foreach itera attraverso i risultati, ma ottiene solo un oggetto alla volta. query utilizza IEnumerator, che ha Reset() e MoveNext(). Il foreach chiama MoveNext() ogni round fino a quando non ci sono più risultati.

Problemi correlati