Saltar al contenido

¿Diferencias entre IQueryable, List, IEnumerator?

Israel, parte de este equipo de trabajo, nos hizo el favor de escribir este artículo ya que controla muy bien dicho tema.

Solución:

IQueryable está destinado a permitir que un proveedor de consultas (por ejemplo, un ORM como LINQ to SQL o Entity Framework) utilice las expresiones contenidas en una consulta para traducir la solicitud a otro formato. En otras palabras, LINQ-to-SQL analiza las propiedades de las entidades que está usando junto con las comparaciones que está haciendo y, de hecho, crea una declaración SQL para expresar (con suerte) una solicitud equivalente.

IEnumerable es más genérico que IQueryable (aunque todas las instancias de IQueryable implementar IEnumerable) y solo define una secuencia. Sin embargo, hay métodos de extensión disponibles dentro del Enumerable clase que define algunos operadores de tipo de consulta en esa interfaz y usa código ordinario para evaluar estas condiciones.

List es solo un formato de salida, y mientras implementa IEnumerable, no está directamente relacionado con la consulta.

En otras palabras, cuando estás usando IQueryable, estás definiendo un expresión que se traduce en otra cosa. Aunque esté escribiendo código, ese código nunca llega ejecutado, solo se pone inspeccionado y se convirtió en otra cosa, como una consulta SQL real. Debido a esto, solo ciertas cosas son válidas dentro de estas expresiones. Por ejemplo, no puede llamar a una función ordinaria que defina desde estas expresiones, ya que LINQ-to-SQL no sabe cómo convertir su llamada en una declaración SQL. La mayoría de estas restricciones solo se evalúan en tiempo de ejecución, desafortunadamente.

Cuando usas IEnumerable para realizar consultas, está utilizando LINQ-to-Objects, lo que significa que está escribiendo el código real que se utiliza para evaluar su consulta o transformar los resultados, por lo que, en general, no hay restricciones sobre lo que puede hacer. Puede llamar a otras funciones desde estas expresiones libremente.

Con LINQ to SQL

De la mano con la distinción anterior, también es importante tener en cuenta cómo funciona esto en la práctica. Cuando escribe una consulta contra una clase de contexto de datos en LINQ to SQL, produce una IQueryable. Hagas lo que hagas en contra de IQueryable sí mismo se convertirá en SQL, por lo que su filtrado y transformación se realizarán en el servidor. Hagas lo que hagas contra esto como un IEnumerable, se realizará a nivel de aplicación. A veces, esto es deseable (en el caso de que necesite utilizar un código del lado del cliente, por ejemplo), pero en muchos casos no es intencional.

Por ejemplo, si tuviera un contexto con un Customers propiedad que representa un Customer mesa, y cada cliente tiene una CustomerId columna, veamos dos formas de hacer esta consulta:

var query = (from c in db.Customers where c.CustomerId == 5 select c).First();

Esto producirá SQL que consulta la base de datos para Customer grabar con un CustomerId igual a 5. Algo como:

select CustomerId, FirstName, LastName from Customer where CustomerId = 5

Ahora, que pasa si nos volvemos Customers en una IEnumerable usando el AsEnumerable() método de extensión?

var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();

Este simple cambio tiene graves consecuencias. Desde que estamos girando Customers en una IEnumerable, esto traerá de vuelta toda la tabla y la filtrará en el lado del cliente (bueno, estrictamente hablando, esto traerá de vuelta todas las filas de la tabla hasta que encuentre uno que se ajuste a los criterios, pero el punto es el mismo).

Listar()

Hasta ahora, solo hemos hablado de IQueryable y IEnumerable. Esto se debe a que son interfaces complementarias similares. En ambos casos, está definiendo un consulta; es decir, estas definiendo donde para encontrar los datos, qué filtros para aplicar, y qué datos para devolver. Ambas son consultas

query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;

Como hemos comentado, la primera consulta usa IQueryable y el segundo usa IEnumerable. En ambos casos, sin embargo, esto es solo una consulta. Definir la consulta en realidad no afecta a la fuente de datos. La consulta se ejecuta realmente cuando el código comienza a iterar sobre la lista. Esto puede suceder de varias formas; a foreach bucle, llamando ToList()etc.

La consulta se ejecuta la primera y cada vez que se repite. Si tuvieras que llamar ToList() sobre query dos veces, terminaría con dos listas con objetos completamente distintos. Pueden contener los mismos datos, pero serían referencias diferentes.

Editar después de los comentarios

Solo quiero aclarar la distinción entre cuándo se hacen las cosas del lado del cliente y cuándo se hacen del lado del servidor. Si hace referencia a un IQueryable como un IEnumerable, solo la consulta hecha después se trata de un IEnumerable se hará en el lado del cliente. Por ejemplo, digamos que tengo esta tabla y un contexto LINQ-to-SQL:

Customer
-----------
CustomerId
FirstName
LastName

Primero construyo una consulta basada en FirstName. Esto crea una IQueryable:

var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;

Ahora paso esa consulta a una función que toma un IEnumerable y hace algunos filtros basados ​​en LastName:

public void DoStuff(IEnumerable customers)

    foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
    
        Console.WriteLine(cust.CustomerId);
    

Hemos realizado una segunda consulta aquí, pero se está haciendo en una IEnumerable. Lo que sucederá aquí es que se evaluará la primera consulta, ejecutando este SQL:

select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'

Así que vamos a traer de vuelta a todos los que FirstName comienza con "Ad". Tenga en cuenta que no hay nada aquí sobre LastName. Eso es porque se filtra del lado del cliente.

Una vez que recupere estos resultados, el programa iterará sobre los resultados y entregará solo los registros cuyo LastName comienza con "Ro". La desventaja de esto es que recuperamos datos, es decir, todas las filas cuyas LastNameno Empezar con "Ro"–ese pudo se han filtrado en el servidor.

IQueryable: resúmenes de acceso a la base de datos, admite la evaluación perezosa de consultas
List: una colección de entradas. Sin soporte de evaluación perezosa
IEnumerator: proporciona la capacidad de iterar una y otra vez IEnumerable (Cual de los dos IQueryable y List son)

El problema con ese código es bastante simple: siempre ejecuta la consulta cuando se llama. Si tuvieras que regresar db.User.Where(...) en su lugar (que es un IQueryable), mantendría la evaluación de la consulta hasta que realmente sea necesaria (iterada). Además, si el usuario de ese método necesita especificar más predicados, estos también se ejecutarán en la base de datos, lo que lo hace mucho más rápido.

Sección de Reseñas y Valoraciones

Recuerda dar visibilidad a este tutorial si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *