Conozco algunas diferencias de LINQ to Entities y LINQ to Objects que el primer implementa IQueryable
y el segundo implementa IEnumerable
y el alcance de mi pregunta está dentro de EF 5.
Mi pregunta es ¿cuál es la diferencia técnica (s) de esos 3 métodos? Veo que en muchas situaciones todos ellos funcionan. También veo el uso de combinaciones de ellos como .ToList().AsQueryable()
.
¿Qué significan exactamente esos métodos?
¿Hay algún problema de rendimiento o algo que lleve al uso de uno sobre otro?
¿Por qué uno usaría, por ejemplo, en
.ToList().AsQueryable()
lugar de.AsQueryable()
?
Respuestas:
Hay mucho que decir sobre esto. Te voy a contar
AsEnumerable
yAsQueryable
y menciónToList()
en el camino.¿Qué hacen estos métodos?
AsEnumerable
yAsQueryable
emitir o convertirIEnumerable
aoIQueryable
, respectivamente. Digo emitir o convertir con una razón:Cuando el objeto de origen ya implementa la interfaz de destino, el objeto de origen se devuelve pero se convierte en la interfaz de destino. En otras palabras: el tipo no cambia, pero el tipo en tiempo de compilación sí.
Cuando el objeto de origen no implementa la interfaz de destino, el objeto de origen se convierte en un objeto que implementa la interfaz de destino. Por lo tanto, tanto el tipo como el tipo de tiempo de compilación cambian.
Déjame mostrarte esto con algunos ejemplos. Tengo este pequeño método que informa el tipo de tiempo de compilación y el tipo real de un objeto ( cortesía de Jon Skeet ):
Probemos con un arbitrario linq-to-sql
Table<T>
, que implementaIQueryable
:El resultado:
Verá que la clase de tabla en sí misma siempre se devuelve, pero su representación cambia.
Ahora un objeto que implementa
IEnumerable
, noIQueryable
:Los resultados:
Ahí está.
AsQueryable()
ha convertido la matriz en anEnumerableQuery
, que "representa unaIEnumerable<T>
colección comoIQueryable<T>
fuente de datos". (MSDN)¿Cual es el uso?
AsEnumerable
se usa con frecuencia para cambiar de cualquierIQueryable
implementación a LINQ a objetos (L2O), principalmente porque la primera no admite funciones que tiene L2O. Para obtener más detalles, consulte ¿Cuál es el efecto de AsEnumerable () en una entidad LINQ? .Por ejemplo, en una consulta de Entity Framework solo podemos usar un número restringido de métodos. Entonces, si, por ejemplo, necesitamos usar uno de nuestros propios métodos en una consulta, típicamente escribiríamos algo como
ToList
- que convierte an enIEnumerable<T>
aList<T>
- a menudo también se usa para este propósito. La ventaja de usarAsEnumerable
vs.ToList
es queAsEnumerable
no ejecuta la consulta.AsEnumerable
conserva la ejecución diferida y no crea una lista intermedia a menudo inútil.Por otro lado, cuando se desea la ejecución forzada de una consulta LINQ,
ToList
puede ser una forma de hacerlo.AsQueryable
se puede usar para hacer que una colección enumerable acepte expresiones en sentencias LINQ. Vea aquí para más detalles: ¿Realmente necesito usar AsQueryable () en la colección? .Nota sobre abuso de sustancias!
AsEnumerable
Funciona como una droga. Es una solución rápida, pero tiene un costo y no aborda el problema subyacente.En muchas respuestas de Stack Overflow, veo personas que solicitan
AsEnumerable
solucionar casi cualquier problema con métodos no compatibles en expresiones LINQ. Pero el precio no siempre es claro. Por ejemplo, si haces esto:... todo está perfectamente traducido en una declaración SQL que filtra (
Where
) y proyecta (Select
). Es decir, tanto la longitud como el ancho, respectivamente, del conjunto de resultados SQL se reducen.Ahora suponga que los usuarios solo quieren ver la parte de la fecha
CreateDate
. En Entity Framework descubrirá rápidamente que ...... no es compatible (en el momento de la escritura). Ah, afortunadamente está la
AsEnumerable
solución:Claro, corre, probablemente. Pero arrastra toda la tabla a la memoria y luego aplica el filtro y las proyecciones. Bueno, la mayoría de las personas son lo suficientemente inteligentes como para hacer lo
Where
primero:Pero aún así todas las columnas se obtienen primero y la proyección se realiza en la memoria.
La solución real es:
(Pero eso requiere un poco más de conocimiento ...)
¿Qué NO hacen estos métodos?
Restaurar las capacidades de IQueryable
Ahora una advertencia importante. Cuando tu lo hagas
terminarás con el objeto fuente representado como
IQueryable
. (Porque ambos métodos solo lanzan y no convierten).Pero cuando lo haces
¿Cuál será el resultado?
El
Select
produce aWhereSelectEnumerableIterator
. Esta es una clase interna .Net que implementaIEnumerable
, noIQueryable
. Por lo tanto, se ha realizado una conversión a otro tipo y el subsiguienteAsQueryable
ya no puede devolver la fuente original.La implicación de esto es que el uso no
AsQueryable
es una forma de inyectar mágicamente a un proveedor de consultas con sus características específicas en un enumerable. Supongamos que lo hacesLa condición where nunca se traducirá a SQL.
AsEnumerable()
seguido de las instrucciones LINQ corta definitivamente la conexión con el proveedor de consultas del marco de la entidad.Muestro este ejemplo deliberadamente porque he visto preguntas aquí donde las personas, por ejemplo, intentan 'inyectar'
Include
capacidades en una colección llamandoAsQueryable
. Se compila y se ejecuta, pero no hace nada porque el objeto subyacente ya no tiene unaInclude
implementación.Ejecutar
Ambos
AsQueryable
yAsEnumerable
no ejecutan (ni enumeran ) el objeto fuente. Solo cambian su tipo o representación. Ambas interfaces involucradas,IQueryable
yIEnumerable
, no son más que "una enumeración esperando a suceder". No se ejecutan antes de verse obligados a hacerlo, por ejemplo, como se mencionó anteriormente, llamandoToList()
.Eso significa que ejecutar un
IEnumerable
obtenido al invocarAsEnumerable
unIQueryable
objeto, ejecutará el subyacenteIQueryable
. Una posterior ejecución de laIEnumerable
voluntad ejecutará nuevamente elIQueryable
. Lo cual puede ser muy costoso.Implementaciones Específicas
Hasta ahora, esto era solo sobre el
Queryable.AsQueryable
yEnumerable.AsEnumerable
los métodos de extensión. Pero, por supuesto, cualquiera puede escribir métodos de instancia o métodos de extensión con los mismos nombres (y funciones).De hecho, un ejemplo común de un
AsEnumerable
método de extensión específico esDataTableExtensions.AsEnumerable
.DataTable
no implementaIQueryable
oIEnumerable
, por lo que los métodos de extensión regulares no se aplican.fuente
AsQueryable()
menudo se basa en un concepto erróneo. Pero dejaré que se cocine a fuego lento en la parte posterior de mi cabeza por un tiempo y veré si puedo agregar más cobertura sobre esa pregunta.Listar()
AsEnumerable ()
Func<TSource, bool>
AsQueryable ()
Expression<Func<TSource, bool>>
AsQueryable()
lo tanto, generalmente funciona mucho más rápido queAsEnumerable()
al generar T-SQL al principio, que incluye todas las condiciones where en su Linq.fuente
ToList () estará todo en la memoria y luego estará trabajando en ello. entonces, ToList (). where (aplicar algún filtro) se ejecuta localmente. AsQueryable () ejecutará todo de forma remota, es decir, se enviará un filtro a la base de datos para su aplicación. Queryable no hace nada hasta que lo ejecutas. ToList, sin embargo, se ejecuta de inmediato.
Además, mire esta respuesta ¿Por qué usar AsQueryable () en lugar de List ()?.
EDITAR: Además, en su caso, una vez que haga ToList (), cada operación posterior es local, incluido AsQueryable (). No puede cambiar a remoto una vez que comience a ejecutar localmente. Espero que esto lo haga un poco más claro.
fuente
Encontró un mal desempeño en el siguiente código.
Arreglado con
Para un IQueryable, quédese en IQueryable cuando sea posible, intente no ser usado como IEnumerable.
Actualización . Se puede simplificar aún más en una expresión, gracias Gert Arnold .
fuente