Si en la primera entrada a esta serie de artículos sobre Fundamentos de LINQ hacíamos un rápido repaso a los Métodos de extensión, ahora toca el turno de la interfaz sobre la que la mayoría de colecciones del CLR de .NET Framework implementan directa o indirectamente.
Básicamente describiremos la importancia en entender su funcionamiento y como los métodos de extensión de LINQ juegan un papel importante a la hora de ejecutar operaciones sobre una colecciones de objetos (independientemente del proveedor LINQ).
IQueryable<T> e IEnumerable<T>
Si me permitís la expresión, estas dos interfaces son "La Madre del Cordero". Todas y cada una de las operaciones (a través de métodos extensores) que conforman LINQ se ejecutan sobre una de estas dos interfaces. Dicho de otra forma para que una colección pueda ser consultada por LINQ debe implementar, directa o indirectamente IEnumerable<T> (IQueryable<T> implementa a su vez IEnumerable<T> como veremos más adelante). De hecho,muchas de las colecciones genéricas que implementa .NET Framework implementan IEnumerable<T>. Esta interfaz ademite la iteración simple de una colección de tipo T genérico y su firma es:
namespace System.Collections.Generic { public interface IEnumerable<out T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } }
Como podemos observar devuelve un tipo generico IEnumerator<T> cuya firma es:
namespace System.Collections.Generic { public interface IEnumerator<out T> : IDisposable, IEnumerator { new T Current { get; } } }
IQueryable
namespace System.Linq { public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable { } }
Ahora profundicemos en IQueryable:
namespace System.Linq { public interface IQueryable : IEnumerable { Expression Expression { get; } Type ElementType { get; } IQueryProvider Provider { get; } } }
Tal y como podemos ver expone tres propiedades de sólo lectura. Expression devuelve la expresión asociada a la instancia IQueryable, ElementType devuelve el tipo de los elementos y Provider el proveedor de consultas asociada a la instancia.
En definitiva, tanto si vamos a hacer uso de proveedores built-in como si pretendemos crear los nuestros propios es recomendable familiarizarse con ambas interfaces y prestar atención especialmente a la diferencia entre proveedores locales y externos cuyas principales diferencias se exponen a continuación:
Proveedores locales utilizan la interfaz IEnumerable<T> y se ejecutan en memoria. Su implementación es a través de delegados anónimos.
Proveedores remotos utilizan la interfaz IQueryable<T> y se ejecutan generalmente de forma remota. Su implementación se realiza a través de árboles de expresiones.