2011-02-04 15 views
8

Saluti, ho la seguente domanda. Non ho trovato la risposta esatta per questo, ed è davvero interessante per me. Supponiamo di avere il seguente codice che recupera i record dal database (per esportarlo in un file XML, ad esempio).LINQ è pigro-valutato?

var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

foreach (var emp in result) { 
    // Append this record in suitable format to the end of XML file 
} 

Supponiamo che ci sia un milione di record che soddisfano la dove condizione nel codice. Cosa accadrà? Tutti questi dati verranno recuperati immediatamente da SQL Server nella memoria di runtime quando raggiungono il valore foreach, oppure verranno recuperati quindi necessari, il primo record, secondo. In altre parole, LINQ gestisce davvero la situazione con iterazioni attraverso raccolte di grandi dimensioni (vedi il mio post qui per i dettagli)?

In caso negativo, come superare i problemi di memoria in questo caso? Se ho davvero bisogno di attraversare la grande collezione, cosa dovrei fare? Calcola la quantità effettiva di elementi nella raccolta con l'aiuto della funzione Conteggio e successivamente legge i dati dal database con piccole parti. C'è un modo semplice per implementare il paging con il framework LINQ?

risposta

8

Tutti i dati saranno recuperati da SQL Server, in una volta, e messi in memoria. L'unica soluzione a cui posso pensare è di elaborare i dati in blocchi più piccoli (come la pagina usando Skip() e Take()). Ma, naturalmente, questo richiede più colpi a SQL Server.

Qui è un metodo di estensione di paging Linq ho scritto per fare questo:

public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) 
    { 
     return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
    } 
+0

+1 Uso una funzione quasi identica per fare ciò. – alex

+0

Non penso che i maggiori risultati per SQL Server rappresentino un problema di prestazioni così grande, se impostiamo correttamente le dimensioni della pagina. Dovrebbe essere circa 1000 di righe per il mio esempio, penso, e quando l'applicazione sarà principalmente occupata con l'esportazione, non con l'interrogazione del database. –

+0

@ Spirit_1984 - Sono d'accordo con te. Preferirei avere più hit su SQL Server che tentare di caricare milioni di righe in memoria. –

4

Sì, LINQ usa valutazione pigra. Il database verrà interrogato al momento dell'esecuzione di foreach, ma recupererà tutti i dati in una volta sola (sarebbe molto meno efficiente fare milioni di query per un solo risultato alla volta).

Se si è preoccupati di inserire troppi risultati in una volta sola, è possibile utilizzare Skip e Top per ottenere solo un numero limitato di risultati alla volta (quindi impaginando il risultato).

1

Sarà recuperato quando si invoca ToList o metodi simili. esecuzione LINQ ha differito:

Il modo - pur avendo esecuzione differita e caricamento della raccolta completa da una sorgente di dati nel caso di un OR/M o qualsiasi altro provider LINQ - sarà determinato dall'implementatore della sorgente dell'oggetto LINQ.

Ad esempio, alcuni OR/M possono fornire un caricamento lento, e ciò significa che l'intero "elenco di clienti" è simile a un cursore e accede a uno degli elementi (un dipendente) e anche a una proprietà , caricherà solo il dipendente stesso o la proprietà accessibile.

Ma, comunque, queste sono le basi.

EDIT: Ora vedo che è una cosa LINQ-to-SQL ... O non so se l'autore della domanda abbia frainteso LINQ e non sa LINQ non è LINQ-to-SQL, ma è più uno schema e una caratteristica del linguaggio.

0

OK, ora grazie a questo answer Ho un'idea: che ne dici di unire la funzione di prendere una pagina con resa restituita possibilità?Ecco l'esempio di codice:

// This is the original function that takes the page 
public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) { 
    return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
} 

// And here is the function with yield implementation 
public static IEnumerable<TSource> Lazy<TSource>(this IEnumerable<TSource> source, int pageSize) { 
    int pageNumber = 1; 
    int count = 0; 

    do { 
    IEnumerable<TSource> coll = Page(source, pageNumber, pageSize); 
    count = coll.Count(); 
    pageNumber++; 
    yield return coll; 
    } while (count > 0); 
} 


// And here goes our code for traversing collection with paging and foreach 
var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

// Let's use the 1000 page size 
foreach (var emp in Lazy(result, 1000)) { 
    // Append this record in suitable format to the end of XML file 
} 

penso che in questo modo siamo in grado di superare il problema della memoria, ma lasciando la Syntaxis di foreach non così complicato.