c# - entre - Devolviendo IEnumerable<T> frente a IQueryable<T>




que es un iqueryable (10)

¿Cuál es la diferencia entre devolver IQueryable<T> frente a IEnumerable<T> ?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

¿Se postergarán las dos ejecuciones y cuándo debería preferirse una sobre la otra?


Además de las primeras 2 respuestas realmente buenas (por driis y por Jacob):

La interfaz IEnumerable está en el espacio de nombres System.Collections.

El objeto IEnumerable representa un conjunto de datos en la memoria y puede mover estos datos solo hacia adelante. La consulta representada por el objeto IEnumerable se ejecuta de forma inmediata y completa, por lo que la aplicación recibe datos rápidamente.

Cuando se ejecuta la consulta, IEnumerable carga todos los datos, y si necesitamos filtrarla, el filtrado se realiza en el lado del cliente.

La interfaz IQueryable se encuentra en el espacio de nombres System.Linq.

El objeto IQueryable proporciona acceso remoto a la base de datos y le permite navegar a través de los datos en orden directo de principio a fin o en orden inverso. En el proceso de crear una consulta, el objeto devuelto es IQueryable, la consulta está optimizada. Como resultado, se consume menos memoria durante su ejecución, menos ancho de banda de red, pero al mismo tiempo se puede procesar un poco más lentamente que una consulta que devuelve un objeto IEnumerable.

¿Qué elegir?

Si necesita el conjunto completo de datos devueltos, entonces es mejor usar IEnumerable, que proporciona la velocidad máxima.

Si NO necesita el conjunto completo de datos devueltos, sino solo algunos datos filtrados, es mejor usar IQueryable.


Al usar LINQ para Entidades, es importante entender cuándo usar IEnumerable e IQueryable. Si usamos IEnumerable, la consulta se ejecutará inmediatamente. Si usamos IQueryable, la ejecución de la consulta se aplazará hasta que la aplicación solicite la enumeración. Ahora veamos qué se debe considerar al decidir si usar IQueryable o IEnumerable. El uso de IQueryable le da la oportunidad de crear una consulta compleja de LINQ utilizando múltiples declaraciones sin ejecutar la consulta en el nivel de la base de datos. La consulta se ejecuta solo cuando se enumera la consulta LINQ final.


En general, desea conservar el tipo estático original de la consulta hasta que sea importante.

Por este motivo, puede definir su variable como 'var' en lugar de IQueryable<> o IEnumerable<> y sabrá que no está cambiando el tipo.

Si comienzas con un IQueryable<> , normalmente querrás mantenerlo como un IQueryable<> hasta que haya alguna razón convincente para cambiarlo. La razón de esto es que desea dar al procesador de consultas la mayor cantidad de información posible. Por ejemplo, si solo va a usar 10 resultados (ha llamado Take(10) ), entonces quiere que SQL Server sepa eso para que pueda optimizar sus planes de consulta y enviarle solo los datos que usará. .

Una razón convincente para cambiar el tipo de IQueryable<> a IEnumerable<> puede ser que esté llamando a alguna función de extensión que la implementación de IQueryable<> en su objeto particular no puede manejar o maneja de manera ineficiente. En ese caso, es posible que desee convertir el tipo a IEnumerable<> (asignándolo a una variable de tipo IEnumerable<> o utilizando el método de extensión AsEnumerable , por ejemplo) para que las funciones de extensión a las que llama terminen siendo las de Clase Queryable lugar de la clase Queryable .


En términos generales recomendaría lo siguiente:

  • Devuelva IQueryable<T> si desea habilitar al desarrollador utilizando su método para refinar la consulta que devuelve antes de ejecutar.

  • Devuelva IEnumerable si desea transportar un conjunto de Objetos para enumerar.

Imagine un IQueryable como lo que es: una "consulta" de datos (que puede refinar si lo desea). Un IEnumerable es un conjunto de objetos (que ya ha sido recibido o creado) sobre los cuales puede enumerar.


La principal diferencia entre "IEnumerable" e "IQueryable" es sobre dónde se ejecuta la lógica del filtro. Uno se ejecuta en el lado del cliente (en la memoria) y el otro se ejecuta en la base de datos.

Por ejemplo, podemos considerar un ejemplo en el que tenemos 10,000 registros para un usuario en nuestra base de datos y digamos solo 900 de los cuales son usuarios activos, por lo que en este caso si usamos "IEnumerable", primero se cargan los 10,000 registros en la memoria y luego aplica el filtro IsActive en él, que finalmente devuelve los 900 usuarios activos.

Mientras que, por otro lado, en el mismo caso, si usamos "IQueryable", aplicará directamente el filtro IsActive en la base de datos, que directamente desde allí devolverá los 900 usuarios activos.

Link referencia


La respuesta principal es buena, pero no menciona los árboles de expresión que explican "cómo" difieren las dos interfaces. Básicamente, hay dos conjuntos idénticos de extensiones LINQ. Where() , Sum() , Count() , FirstOrDefault() , etc. tienen dos versiones: una que acepta funciones y otra que acepta expresiones.

  • La firma de la versión IEnumerable es: Where(Func<Customer, bool> predicate)

  • La firma de la versión de IQueryable es: Where(Expression<Func<Customer, bool>> predicate)

Probablemente ha estado usando ambos sin darse cuenta porque a ambos se les llama con una sintaxis idéntica:

por ejemplo, Where(x => x.City == "<City>") funciona tanto en IQueryable como en IEnumerable

  • Cuando se utiliza Where() en una colección IEnumerable , el compilador pasa una función compilada a Where()

  • Cuando se utiliza Where() en una colección IQueryable , el compilador pasa un árbol de expresiones a Where() . Un árbol de expresiones es como el sistema de reflexión, pero para el código. El compilador convierte su código en una estructura de datos que describe lo que hace su código en un formato que es fácilmente digerible.

¿Por qué molestarse con esta expresión del árbol? Solo quiero Where() para filtrar mis datos. La razón principal es que tanto los ORM de EF como los de Linq2SQL pueden convertir los árboles de expresión directamente en SQL, donde su código se ejecutará mucho más rápido.

Oh, eso suena como un aumento de rendimiento gratuito, ¿debería usar AsQueryable() todo el lugar en ese caso? No, IQueryable solo es útil si el proveedor de datos subyacente puede hacer algo al respecto. Convertir algo como una List normal a IQueryable no le dará ningún beneficio.


Podemos usar ambos de la misma manera, y solo son diferentes en el rendimiento.

IQueryable solo se ejecuta contra la base de datos de una manera eficiente. Esto significa que crea una consulta de selección completa y solo obtiene los registros relacionados.

Por ejemplo, queremos tomar a los 10 principales clientes cuyo nombre comience con 'Nimal'. En este caso, la consulta de selección se generará select top 10 * from Customer where name like 'Nimal%' .

Pero si usamos IEnumerable, la consulta sería como select * from Customer where name like 'Nimal%' y los diez primeros se filtrarán en el nivel de codificación de C # (obtiene todos los registros de clientes de la base de datos y los pasa a C #) .


Recientemente me encontré con un problema con IEnumerable v. IQueryable . El algoritmo que se usó primero realizó una consulta de IQueryable para obtener un conjunto de resultados. Luego se pasaron a un bucle foreach , con los elementos instanciados como una clase de Entity Framework (EF). Esta clase de EF luego se usó en la cláusula from de una consulta de Linq a entidad, lo que provocó que el resultado fuera IEnumerable .

Soy bastante nuevo en EF y Linq para Entidades, por lo que me tomó un tiempo averiguar cuál era el cuello de botella. Usando MiniProfiling, encontré la consulta y luego convertí todas las operaciones individuales en una única consulta de IQueryable Linq for Entities. El IEnumerable tomó 15 segundos y el IQueryable tomó 0.5 segundos para ejecutarse. Hubo tres tablas involucradas y, después de leer esto, creo que la consulta IEnumerable estaba formando un producto cruzado de tres tablas y filtrando los resultados.

Intente usar IQueryables como regla general y haga un perfil de su trabajo para hacer que sus cambios sean medibles.


Sí, ambos usan ejecución diferida. Vamos a ilustrar la diferencia usando el generador de perfiles de SQL Server ...

Cuando ejecutamos el siguiente código:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

En el analizador de SQL Server encontramos un comando igual a:

"SELECT * FROM [dbo].[WebLog]"

Se tarda aproximadamente 90 segundos en ejecutar ese bloque de código en una tabla de registro web que tiene 1 millón de registros.

Por lo tanto, todos los registros de la tabla se cargan en la memoria como objetos, y luego con cada .Where () habrá otro filtro en la memoria contra estos objetos.

Cuando usamos IEnumerable en lugar de IEnumerable en el ejemplo anterior (segunda línea):

En el analizador de SQL Server encontramos un comando igual a:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

Se tarda aproximadamente cuatro segundos en ejecutar este bloque de código usando IQueryable .

IQueryable tiene una propiedad llamada Expression que almacena una expresión de árbol que comienza a crearse cuando usamos el result en nuestro ejemplo (que se llama ejecución diferida), y al final esta expresión se convertirá en una consulta SQL para ejecutar en el motor de base de datos. .


Se ha dicho mucho anteriormente, pero volviendo a las raíces, de una manera más técnica:

  1. IEnumerable es una colección de objetos en la memoria que puede enumerar , una secuencia en la memoria que hace posible iterar a través (hace que sea muy fácil dentro del bucle foreach , aunque solo puede IEnumerator ). Ellos residen en la memoria como es.
  2. IQueryable es un árbol de expresiones que se traducirá en otra cosa en algún momento con capacidad para enumerar sobre el resultado final . Supongo que esto es lo que confunde a la mayoría de la gente.

Obviamente tienen diferentes connotaciones.

IQueryable representa un árbol de expresiones (una consulta, simplemente) que el proveedor de consultas subyacente traducirá a otra cosa tan pronto como se llamen las API de lanzamiento, como las funciones agregadas de LINQ (Sum, Count, etc.) o ToList [Array, Dictionary, ...]. Y los objetos IEnumerable también implementan IEnumerable , IEnumerable<T> modo que si representan una consulta, el resultado de esa consulta podría ser iterado. Significa que IQueryable no tiene que ser solo consultas. El término correcto es que son árboles de expresión .

Ahora, cómo se ejecutan esas expresiones y a qué se dirigen, todo depende de los denominados proveedores de consultas (ejecutores de expresiones en los que podemos pensar).

En el mundo de Entity Framework (que es el proveedor de origen de datos subyacente místico o el proveedor de consultas), las expresiones IQueryable se convierten en consultas T-SQL nativas. Nhibernate hace cosas similares con ellos. Puede escribir su propio siguiendo los conceptos bastante bien descritos en LINQ: Crear un enlace de proveedor IQueryable , por ejemplo, y es posible que desee tener una API de consulta personalizada para el servicio de proveedor de su tienda de productos.

Básicamente, los objetos IQueryable se están construyendo todo el tiempo hasta que los liberamos explícitamente y le decimos al sistema que los reescriba en SQL o lo que sea y enviamos la cadena de ejecución para su procesamiento posterior.

Como si se tratara de una ejecución diferida , es una característica de LINQ mantener el esquema del árbol de expresiones en la memoria y enviarlo a la ejecución solo a pedido, siempre que se llame a ciertas API contra la secuencia (la misma cuenta, lista de tareas, etc.).

El uso adecuado de ambos depende en gran medida de las tareas que enfrenta para el caso específico. Para el conocido patrón de repositorio, personalmente opto por devolver IList , es decir IEnumerable sobre Listas (indexadores y similares). Por lo tanto, es mi consejo usar IQueryable solo en los repositorios e IEnumerable en cualquier otra parte del código. No estoy diciendo acerca de las preocupaciones de IQueryable que IQueryable rompe y arruina el principio de separación de preocupaciones . Si devuelve una expresión desde dentro de los repositorios, los consumidores pueden jugar con la capa de persistencia como deseen.

Una pequeña adición al lío :) (de una discusión en los comentarios)) Ninguno de ellos son objetos en la memoria, ya que no son tipos reales en sí mismos, son marcadores de un tipo, si quieres profundizar tanto. Pero tiene sentido (y es por eso que incluso MSDN puso de esta manera) pensar en IEnumerables como colecciones en memoria mientras que IQueryables como árboles de expresión. El punto es que la interfaz IQueryable hereda la interfaz IEnumerable, de modo que si representa una consulta, los resultados de esa consulta pueden ser enumerados. La enumeración hace que se ejecute el árbol de expresiones asociado con un objeto IQueryable. Entonces, de hecho, no puede llamar a ningún miembro de IEnumerable sin tener el objeto en la memoria. Llegará allí si lo hace, de todos modos, si no está vacío. Los IQueryables son solo consultas, no los datos.





iqueryable