2010-08-02 9 views
40

consideran este código:¿Entiendo mal LINQ to SQL .AsEnumerable()?

var query = db.Table 
       .Where(t => SomeCondition(t)) 
       .AsEnumerable(); 

int recordCount = query.Count(); 
int totalSomeNumber = query.Sum(); 
decimal average = query.Average(); 

Supongamos query tarda mucho tiempo en ejecutarse. Necesito obtener el recuento de registros, total de SomeNumber devuelto, y tomar un promedio al final. Pensé basándome en mi lectura que .AsEnumerable() ejecutaría la consulta usando LINQ-to-SQL, luego usar LINQ-to-Objects para el Count, Sum y Average. En cambio, cuando hago esto en LINQPad, veo que la misma consulta se ejecuta tres veces. Si reemplazo .AsEnumerable() con .ToList(), solo se realiza una consulta una vez.

Me falta algo acerca de lo que AsEnumerable es/does?

+0

Una pregunta muy útil para entender el comportamiento de 'AsEnumerable()' – Lijo

Respuesta

51

Llamando AsEnumerable() no ejecuta la consulta, enumerando lo hace.

IQueryable es la interfaz que permite que LINQ to SQL realice su magia. IQueryable implementa IEnumerable por lo que cuando se llama a AsEnumerable(), que está cambiando la extensión-métodos que se denominan a partir de ahí, es decir, a partir de los IQueryable -Métodos a las IEnumerable -Métodos (es decir, cambiar LINQ to SQL-LINQ to Objects en este caso particular). Pero usted es no ejecutando la consulta real, simplemente cambiando cómo se va a ejecutar en su totalidad.

Para forzar la ejecución de la consulta, debe llamar al ToList().

+40

Ciertamente es incorrecto pensar que "nada sucede realmente". Mientras 'AsEnumerable' no evalúa la consulta en el momento en que se llama, * definitivamente * tiene un efecto. Cualquier otra consulta sobre la consulta se evaluará utilizando LINQ para los objetos, por lo que no puede componer elementos adicionales en la consulta (otro 'Donde' o un' OrderBy' o algo de esa naturaleza) que formarán parte de la declaración SQL. –

+1

Un gran ejemplo de lo que hace esto y por qué quieres usarlo: supongamos que estás creando una consulta y el primer bloque termina con 'OrderBy (...)', el tipo ahora es 'IOrderedEnumerable' para luego bajarlo el camino puede seguir añadiendo 'ThenBy (...)' e incluso más tarde puede decir 'return originalQuery.AsEnumerable()' para devolverlo a un 'IEnumerable' normal –

+1

Prefiero ToArray que hace lo mismo, a menos que específicamente necesita la implementación de la lista . –

10

Sí. Todo lo que hará AsEnumerable es hacer que las funciones Count, Sum y Average se ejecuten en el lado del cliente (en otras palabras, devolverá todo el conjunto de resultados al cliente, entonces el cliente realizará esos agregados en lugar de crear COUNT()SUM() y AVG() declaraciones en SQL).

+0

Pero el punto de la OP es que él asumió lo que dice es cierto, pero la prueba empírica demuestre que no lo es. –

+0

-1 Esto simplemente no es verdad. IQueryable implementa IEnumerable por lo que la llamada a AsEnumerable no es operativa y no fuerza la ejecución de consultas. –

+8

@James, Justin: Me malinterpretas. Nunca dije que 'AsEnumerable()' haría que la consulta evaluara, dije que lo único que agregaría sería que * cuando * los agregados se evalúen, se realizarán en el lado del cliente (el conjunto de resultados completo se enumerará * en el cliente *, y se calculará el agregado) en lugar de traducirse a una declaración de SQL. –

0

Supongo que ToList obliga a Linq a recuperar los registros de la base de datos. Cuando realice los cálculos anteriores, se realizarán contra los objetos en memoria en lugar de involucrar a la base de datos.

Dejar el tipo de devolución como Enumerable significa que los datos no se obtienen hasta que el código que realiza los cálculos los llama. Supongo que el problema es que la base de datos se golpea tres veces, una para cada cálculo y los datos no se conservan en la memoria.

2

Bueno, estás en el camino correcto. El problema es que un IQueryable (cuál es la declaración antes de la llamada AsEnumerable) también es un IEnumerable, por lo que esa llamada es, en efecto, un nop. Se requerirá forzarlo a una estructura de datos en memoria específica (por ejemplo, ToList()) para forzar la consulta.

3

Justin Niessner's answer es perfecto.

sólo quiero citar una explicación de MSDN aquí: .NET Language-Integrated Query for Relational Data

El operador AsEnumerable(), a diferencia de ToList() y ToArray(), no causa la ejecución de la consulta. Todavía es diferido.El operador AsEnumerable() simplemente cambia el tipado estático de la consulta, convirtiendo un IQueryable en un IEnumerable, engañando al compilador para que trate el resto de la consulta como ejecutada localmente.

espero que esto es lo que se entiende por:

IQueryable-métodos a las IEnumerable-métodos (es decir, cambiando de LINQ to SQL para LINQ a Objetos

Una vez que está LINQ a Objetos podemos aplicar los métodos del objeto (ej. ToString()). Esta es la explicación de una de las preguntas más frecuentes sobre LINQ - Why LINQ to Entities does not recognize the method 'System.String ToString()?

Acco rding a ASENUMERABLE - codeblog.jonskeet, AsEnumerable puede ser útil cuando:

algunos aspectos de la consulta en la base de datos, y luego un poco más la manipulación en .NET - sobre todo si hay aspectos que, básicamente, no se puede poner en práctica en LINQ a SQL (o el proveedor que estés usando).

También dice:

Todo lo que estamos haciendo es cambiar el tipo en tiempo de compilación de la secuencia que se propagan a través de nuestra consulta desde IQueryable a IEnumerable - pero eso significa que el compilador utilizará los métodos en Enumerable (tomando delegados, y ejecutando en LINQ to Objects) en lugar de los que están en Queryable (tomando árboles de expresión, y generalmente ejecutando out-of-process).

Por último, también se ven esta pregunta relacionada: Returning IEnumerable vs. IQueryable