2011-02-04 10 views
8

Saludos, Tengo la siguiente pregunta. No encontré la respuesta exacta para eso, y es realmente interesante para mí. Supongamos que tengo el siguiente código que recupera los registros de la base de datos (para exportarlo a un archivo XML, por ejemplo).¿LINQ es evaluado de forma diferida?

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 
} 

Supongamos que hay un millón de registros que satisfacen la condición donde en el código. ¿Lo que sucederá? Todos estos datos se recuperarán de SQL Server de inmediato en la memoria de tiempo de ejecución cuando llegue al foreach constructo, o se recuperará entonces necesario, el primer registro, segundo. En otras palabras, ¿LINQ realmente maneja la situación al iterar a través de grandes colecciones (ver mi post aquí para más detalles)?

Si no, ¿cómo solucionar los problemas de memoria en ese caso? Si realmente necesito atravesar la gran colección, ¿qué debo hacer? Calcule la cantidad real de elementos en la colección con la ayuda de la función Contar, y luego lea los datos de la base de datos en pequeñas porciones. ¿Hay alguna manera fácil de implementar paginación con LINQ framework?

Respuesta

8

Todos los datos se recuperarán de SQL Server, a la vez, y se guardarán en la memoria. La única forma de evitar esto es pensar en procesar los datos en fragmentos más pequeños (como la página que utiliza Omitir() y Tomar()). Pero, por supuesto, esto requiere más visitas a SQL Server.

Aquí es un método de extensión de paginación LINQ escribí para hacer esto:

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 función casi idéntica para hacer esto. – alex

+0

No creo que más visitas a SQL Server supongan un problema de rendimiento tan grande si configuramos el tamaño de página correctamente. Debería estar alrededor de 1000 filas para mi ejemplo, creo, y cuando la aplicación estará principalmente ocupada con la exportación, no con consultar la base de datos. –

+0

@ Spirit_1984 - Estoy de acuerdo contigo. Prefiero tener más visitas a SQL Server que intentar cargar millones de filas en la memoria. –

4

Sí, LINQ utiliza la evaluación perezosa. Se consultará la base de datos cuando foreach comience a ejecutarse, pero buscará todos los datos de una vez (sería mucho menos eficiente hacer millones de consultas para un solo resultado a la vez).

Si le preocupa obtener demasiados resultados de una sola vez, puede usar Omitir y Superior para obtener solo un número limitado de resultados a la vez (paginando así el resultado).

1

Se recuperará cuando invoque ToList o métodos similares. ejecución LINQ ha diferido:

La forma - ni siquiera tener ejecución diferida y la carga de toda la colección a partir de una fuente de datos en el caso de una O/M o cualquier otro proveedor LINQ - será determinado por el implementador de la fuente del objeto LINQ.

Eso es, por ejemplo, algunos OR/M pueden proporcionar carga lenta, y eso significa que su "lista completa de clientes" sería algo así como un cursor y acceder a uno de los elementos (un empleado) y también una propiedad cargará solo al empleado o la propiedad accedida.

Pero, de todos modos, estos son los conceptos básicos.

EDIT: ahora veo que es una cosa LINQ-to-SQL ... O no sé si el autor de la pregunta no entendió LINQ y no sabe que LINQ no es LINQ-to-SQL, pero es más un patrón y una función de idioma.

0

bien, ahora gracias a esta answer tengo una idea - ¿qué hay de mezclar la función de tomar una página con yield return posibilidades?Aquí está la muestra de código:

// 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 
} 

creo que de esta manera podemos superar el problema de memoria, sin embargo, dejar la sintaxis de foreach no tan complicado.

Cuestiones relacionadas