que entre diferencia and c# linq linq-to-sql ienumerable iqueryable

c# - entre - que es asqueryable



Devolviendo IEnumerable<T> frente a IQueryable<T> (15)

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.

¿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?


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.


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

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

Devolver un IEnumerable automáticamente el tiempo de ejecución a usar LINQ a los Objetos para consultar su colección.

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


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.


Hay una publicación de blog con un breve ejemplo de código fuente acerca de cómo el uso indebido de IEnumerable<T> puede afectar drásticamente el rendimiento de la consulta LINQ: Entity Framework: IQueryable vs. IEnumerable .

Si profundizamos y observamos las fuentes, podemos ver que obviamente existen diferentes métodos de extensión 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); } }

e 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 la fuente IQueryable .


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.


Me gustaría aclarar algunas cosas debido a las respuestas aparentemente conflictivas (principalmente en torno a IEnumerable).

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

(2) Tanto IEnumerable como IQueryable LINQ intentan realizar una carga perezosa al iterar 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, los 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 recuperado, posiblemente con pereza) porque no tiene un proveedor de datos abstractos. IQueryables basa en un proveedor abstracto (como LINQ-to-SQL), aunque este también podría ser el proveedor de .NET en memoria.

Caso de uso de muestra

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

(b) Pase el IQueryable a una vista cuyo modelo sea IEnumerable . (Válido. IEnumerable extiende IQueryable .)

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

Posibles problemas

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

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

(3) Conjuntos de resultados activos múltiples (MARS). Si está iterando sobre el IEnumerable en un IEnumerable foreach( var record in resultSet ) y al mismo tiempo intenta acceder a record.childEntity.childProperty , puede terminar con MARS debido a la carga record.childEntity.childProperty tanto del conjunto de datos como de la entidad relacional. Esto causará una excepción si no está habilitado en su cadena de conexión.

Solución

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

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 la memoria.

En los casos en los que accede a entidades relacionadas, es posible que aún necesite un contexto de datos. O eso, o puede desactivar los proxies de entidad e Include explícitamente las entidades relacionadas desde su DbSet .


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 te darán una ejecución diferida .

La diferencia es que IQueryable<T> es la interfaz que permite que LINQ-to-SQL (LINQ.-to-anything) funcione. Entonces, si sigue afinando su consulta en un IQueryable<T> , esa consulta se ejecutará en la base de datos, si es posible.

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

En codigo:

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

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

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

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


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.


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