Volviendo IEnumerable vs IQueryable


¿Cuál es la diferencia entre volver IQueryable<T> vs 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;

Ambos serán de ejecución diferida y ¿cuándo se debe preferir uno sobre el otro?

Author: Peter Mortensen, 2010-05-20

15 answers

Sí, ambos le darán ejecución diferida.

La diferencia es que IQueryable<T> es la interfaz que permite LINQ to SQL (LINQ.- a-cualquier cosa realmente) para trabajar. Así que si refinas aún más tu consulta en un IQueryable<T>, esa consulta se ejecutará en la base de datos, si es posible.

Para el IEnumerable<T> caso, será LINQ-to-object, lo que significa que todos los objetos que coincidan con la consulta original tendrán que cargarse en la memoria desde el base.

En el código:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Ese código ejecutará SQL para seleccionar solo clientes gold. El siguiente código, por otro lado, ejecutará la consulta original en la base de datos, luego filtrará los clientes no gold en la memoria:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Esta es una diferencia bastante importante, y trabajando en IQueryable<T> puede en muchos casos salvarle de devolver demasiadas filas de la base de datos. Otro ejemplo excelente es hacer paginación: Si usa Take y Skip on IQueryable, solo obtendrá el número de filas solicitadas; haciendo eso en un IEnumerable<T> hará que todas sus filas se carguen en la memoria.

 1556
Author: driis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-18 07:32:15

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

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

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

Probablemente has estado usando ambos sin dándolo cuenta porque ambos son llamados usando una sintaxis idéntica:

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

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

  • Cuando se usa 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 tu código en una estructura de datos que describe lo que hace tu código en un formato fácil de digerir.

¿Por qué molestarse con esto del árbol de expresión? Solo quiero Where() filtrar mis datos. La razón principal es que tanto EF como Linq2Sql OR pueden convertir á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, ¿debo usar AsQueryable() por todo el lugar en ese caso? No, IQueryable solo es útil si el proveedor de datos subyacente puede hacer algo consigo. Convertir algo como un List regular a IQueryable no le dará ningún beneficio.

 212
Author: Jacob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-18 07:40:55

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 SQL Server profiler encontramos un comando igual a:

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

Se tarda aproximadamente 90 segundos en ejecutar ese bloque de código contra una tabla de WebLog 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 uno .Where() será otro filtro en memoria contra estos objeto.

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

En SQL Server profiler 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.

 62
Author: Kasper Roma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 22:13:51

Ambos le darán ejecución diferida, sí.

En cuanto a cuál se prefiere sobre el otro, depende de cuál es su fuente de datos subyacente.

Devolver unerableumerable forzará automáticamente al tiempo de ejecución a usar LINQ to Objects para consultar su colección.

Devolver un IQueryable (que implementaEnumerable, por cierto) proporciona la funcionalidad adicional para traducir su consulta en algo que podría funcionar mejor en el origen subyacente (LINQ a SQL, LINQ a XML, etc.).

 54
Author: Justin Niessner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-25 22:55:08

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

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

Si comienza con un IQueryable<>, normalmente desea mantenerlo como un IQueryable<> hasta que haya alguna razón convincente para cambiarlo. La razón de esto es que desea darle al procesador de consultas tanta información como sea posible. Para por ejemplo, si solo vas a usar 10 resultados (has llamado Take(10)), entonces quieres que SQL Server lo sepa para que pueda optimizar sus planes de consulta y enviarte solo los datos que usarás.

Una razón convincente para cambiar el tipo de IQueryable<> a IEnumerable<> podría 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<> (asignando a una variable de tipo IEnumerable<> o usando el método de extensión AsEnumerable por ejemplo) para que las funciones de extensión que llame terminen siendo las de la clase Enumerable en lugar de la clase Queryable.

 23
Author: AJS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-15 13:27:47

En términos generales, recomendaría lo siguiente:

Devolver IQueryable si desea habilitar al desarrollador usando su método para refinar la consulta que devuelve antes de ejecutarla.

Si desea transportar solo un conjunto de objetos para enumerar, simplemente tome Iumerable.

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 fue creado) sobre el que se puede enumerar.

 22
Author: sebastianmehler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-26 16:41:09

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

  1. IEnumerable es una colección de objetos en memoria que puede enumerar - una secuencia en memoria que hace posible iterar a través (hace que sea fácil para dentro del bucle foreach, aunque solo puede ir con IEnumerator). Residen en la memoria tal como está.
  2. IQueryable es un árbol de expresiones que se traducirá a otra cosa en algún momento con la capacidad de 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 será traducido a otra cosa por el proveedor de consultas subyacente tan pronto como se llamen las API de la versión, como las funciones agregadas de LINQ (Suma, Recuento, etc.).) o ToList [Array, Dictionary,...]. Y los objetos IQueryable también implementan IEnumerable, IEnumerable<T> así que si representan un 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é recurren depende de los llamados proveedores de consultas (ejecutores de expresiones en los que podemos pensar).

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

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

Como si aplazado ejecución es una característica LINQ para mantener el esquema del árbol de expresiones en la memoria y enviarlo a la ejecución solo bajo demanda, siempre que se llamen ciertas API contra la secuencia (el mismo Conteo, ToList, etc.).).

El uso adecuado de ambos depende en gran medida de las tareas que se enfrentan para el caso específico. Para el conocido patrón de repositorio personalmente opto por devolver IList, eso es IEnumerable sobre Listas (indexadores y similares). Por lo tanto, es mi consejo usar IQueryable solo dentro de repositorios eumumerable en cualquier otro lugar del código. No decir sobre las preocupaciones de probabilidad que IQueryable rompe y arruina el principio de separación de preocupaciones. Si devuelve una expresión desde 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 es objetos en memoria ya que no son tipos reales per se, son marcadores de un tipo - si quieres ir tan profundo. Pero tiene sentido (y es por eso que incluso MSDN lo puso de esta manera) pensar enEnumerables como colecciones en memoria mientras que IQueryables como árboles de expresión. El punto es que la interfaz IQueryable hereda la interfazerableumerable de modo que si representa una consulta, los resultados de esa consulta se pueden enumerar. La enumeración causa el árbol de expresiones asociado con un Objeto IQueryable a ejecutar. Así que, de hecho, realmente no se puede llamar a cualquier IEnumerable miembro sin tener el objeto en la memoria. Se meterá ahí si lo haces, de todos modos, si no está vacío. IQueryables son solo consultas, no los datos.

 18
Author: Arman McHitarian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 22:10:32

Hay una entrada de blog con un breve ejemplo de código fuente sobre cómo el mal uso de IEnumerable<T> puede afectar dramáticamente el rendimiento de la consulta LINQ: Entity Framework: IQueryable vs .Enumerable.

Si profundizamos y miramos las fuentes, podemos ver que obviamente hay diferentes métodos de extensión que se realizan para IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

Y IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

El primero devuelve un iterador enumerable, y el segundo crea una consulta a través del proveedor de consultas, especificado en IQueryable fuente.

 17
Author: Olexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-23 22:45:22

Recientemente me encontré con un problema conEnumerable v. IQueryable. El algoritmo utilizado primero realizó una consulta IQueryable para obtener un conjunto de resultados. Estos fueron luego pasados a un bucle foreach, con los elementos instanciados como una clase EF. Esta clase EF se usó en la cláusula from de una consulta Linq to Entity, haciendo que el resultado fueraumumerable. Soy bastante nuevo en EF y Linq para Entidades, así que me llevó un tiempo descubrir cuál era el cuello de botella. Usando MiniProfiling, encontré la consulta y luego convirtió todas las operaciones individuales en una sola consulta IQueryable Linq para Entidades. La interfaz IEnumerable tomó 15 segundos y el IQueryable llevó a 0.5 segundos. Había tres tablas involucradas y, después de leer esto, creo que la consultaerableumerable estaba realmente formando un producto cruzado de tres tablas y filtrando los resultados.

Intente usar IQueryables como regla general y perfile su trabajo para hacer que sus cambios sean medibles.

 9
Author: sscheider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-13 07:17:08

Me gustaría aclarar algunas cosas debido a respuestas aparentemente contradictorias (en su mayoría alrededor deerableumerable).

(1) IQueryable extiende la interfaz IEnumerable. (Puedes enviar un IQueryable a algo que espera IEnumerable sin error.)

(2) Tanto IQueryable como IEnumerable LINQ intentan cargar perezosamente cuando iteran sobre el conjunto de resultados. (Tenga en cuenta que la implementación se puede ver en los métodos de extensión de interfaz para cada tipo.)

En otras palabras, IEnumerables no son exclusivamente "en memoria". IQueryables no siempre se ejecutan en la base de datos. IEnumerable debe cargar cosas en la memoria (una vez recuperadas, posiblemente perezosamente) porque no tiene un proveedor de datos abstracto. IQueryables depende de un proveedor abstracto (como LINQ-to-SQL), aunque también podría ser el proveedor.NET en memoria.

Ejemplo de caso de uso

(a) Recuperar la lista de registros como IQueryable del contexto EF. (No hay registros en memoria.)

(b) Pase el IQueryable a una vista cuyo modelo es IEnumerable. (Valido. IQueryable extiende IEnumerable.)

(c) Iterar y acceder a los registros del conjunto de datos, entidades secundarias y propiedades desde la vista. (Puede causar excepciones!)

Posibles cuestiones

(1) El IEnumerable intenta la carga lenta y su contexto de datos ha caducado. Excepción lanzada porque el proveedor ya no está disponible.

(2) Entity Framework los proxies de entidad están habilitados (el valor predeterminado), y usted intenta acceder a un objeto relacionado (virtual) con un contexto de datos caducado. Igual que (1).

(3) Múltiples Conjuntos de Resultados Activos (MARS). Si está iterando sobre el IEnumerable en un bloque foreach( var record in resultSet ) y simultáneamente intenta acceder a record.childEntity.childProperty, puede terminar con MARS debido a la carga lenta tanto del conjunto de datos como de la entidad relacional. Esto causará una excepción si no está habilitada en su cadena de conexión.

Solución

  • He encontrado que habilitar MARS en la cadena de conexión funciona de manera poco fiable. Te sugiero que evites MARTE a menos que sea bien entendido y deseado explícitamente.

Ejecute la consulta y almacene los resultados invocando resultList = resultSet.ToList() Esta parece ser la forma más sencilla de garantizar que sus entidades estén en memoria.

En los casos en los que está accediendo a entidades relacionadas, aún puede requerir un contexto de datos. Ya sea eso, o puede deshabilitar proxies de entidad y explícitamente Include entidades relacionadas desde su DbSet.

 9
Author: Alexander Pritchard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-09 19:13:16

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

Por ejemplo, podemos considerar un ejemplo donde tenemos 10,000 registros para un usuario en nuestra base de datos y digamos que solo 900 son usuarios activos, por lo que en este caso si usamos "Enumerable", primero carga todos los 10,000 registros en memoria y luego aplica el filtro isActive en 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.

Referencia Enlace

 7
Author: Tabish Usman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-25 05:29:48

Estas son algunas diferencias entre IQueryable<T> y IEnumerable<T>

la diferencia entre volver IQueryable vs IEnumerable

 6
Author: Basheer AL-MOMANI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-13 05:34:18

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

IQueryable solo se ejecuta contra la base de datos de manera eficiente. Significa que crea una consulta select completa y solo obtiene los registros relacionados.

Por ejemplo, queremos tomar el top 10 clientes cuyo nombre comienza con 'Nimal'. En este caso, la consulta select se generará como select top 10 * from Customer where name like ‘Nimal%’.

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

 4
Author: user3710357,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 22:17:06

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

IEnumerable la interfaz está en el Sistema.Espacio de nombres de colecciones.

El objetoumumerable representa un conjunto de datos en memoria y solo puede avanzar con estos datos. La consulta representada por el objetoerableumerable se ejecuta de forma inmediata y completa, por lo que la aplicación recibe datos rápidamente.

Cuando se ejecuta la consulta, loadsumerable carga todos los datos, y si necesitamos filtrar se, el filtrado en sí se realiza en el lado del cliente.

La interfaz IQueryable se encuentra en el Sistema.Espacio de nombres Linq.

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

, ¿Qué elegir?

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

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

 4
Author: Gleb B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 10:29:22

Al usar LINQ to Entities, es importante entender cuándo el uso de IEnumerable y IQueryable . Si utilizamos IEnumerable, la consulta se ejecutará inmediatamente. Si utilizamos IQueryable, el la ejecución de la consulta se diferirá hasta que la aplicación solicite el enumeración. Ahora vamos a ver lo que debe considerarse al decidir si uso IQueryable o IEnumerable. El uso de IQueryable le da una oportunidad de crear una consulta LINQ compleja usando varias sentencias sin ejecutar la consulta en el nivel de base de datos. La consulta obtiene se ejecuta solo cuando se enumera la consulta LINQ final.

 -4
Author: Orel Hernandez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-10 16:34:22