objects - .NET Entity Framework-IEnumerable VS. IQueryable
include entity framework core (4)
IEnumerable no se hidratará hasta que se materialice. Si llamo a un procedimiento almacenado, creo que no se requiere un filtro adicional, me refiero a que envía parámetros a un procedimiento almacenado para producir el subconjunto deseado de datos devueltos. IEnumerable atado a un procedimiento almacenado está bien. Sin embargo, si está obteniendo todo el contenido de una tabla y luego filtrando la aplicación, debería tener una estrategia. Al igual que, no ToList () el IEnumerable de la tabla, materializará todas las filas. A veces esto causará una excepción de memoria insuficiente. Además de por qué consumir memoria sin razón. Use IQueryable en el contexto, de esa manera puede filtrar la tabla en el origen de datos y no en la aplicación. En respuesta, hay que materializarlo. IEnumerable es una interfaz, solo materializándola "inicializará" su tipo y producirá algo en mi entendimiento.
Por favor vea esta línea de código. Esta es una invocación de un procedimiento almacenado, que devuelve un ObjectResult<long?>
. Para extraer los valores largos agregué el Select:
dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value);
Basado en la inteligencia, este Select devuelve IEnumerable<long>
.
No estoy seguro de si lo leí en algún lugar o quizás me acostumbré a esta suposición: siempre pensé que cuando la API de EF devuelve un IEnumerable
(y no IQueryable
), esto significa que los resultados se han materializado. Lo que significa que han sido sacados de la base de datos.
Hoy descubrí que estaba equivocado (¿o tal vez es un error?). Seguí recibiendo el error
"La nueva transacción no está permitida porque hay otros subprocesos ejecutándose en la sesión"
Básicamente, este error le indica que está intentando guardar cambios mientras el lector de db todavía está leyendo registros.
Eventualmente lo resolví (lo que consideré una posibilidad muy ToArray()
) y ToArray()
llamada ToArray()
para materializar el IEnumerable<long>
...
Entonces, en el fondo, ¿debería esperar que los resultados de IEnumerable
de EF contengan resultados que aún no se han materializado? En caso afirmativo, ¿hay alguna manera de saber si un IEnumerable
se ha materializado o no?
Gracias y disculpas si esta es una de esas preguntas ''duhhh'' ... :)
IEnumerable: LINQ to Object y LINQ to XML.
IQueryable: LINQ to SQL
Trabajar en un IEnumerable<T>
significa que todas las demás operaciones se realizarán en el código C #, es decir, linq-to-objects. No significa que la consulta ya se haya ejecutado.
Una vez que haya degradado a linq-to-objects, todos los datos que queden en este punto deben recuperarse de la base de datos y enviarse a .net. Esto puede degradar drásticamente el rendimiento (por ejemplo, los índices de base de datos no serán utilizados por linq-to-objects), pero por otro lado linq-to-objects es más flexible, ya que puede ejecutar código C # arbitrario en lugar de estar limitado por lo que su proveedor de linq puede traducir a SQL.
Una IEnumerable<T>
puede ser una consulta diferida o datos ya materializados. Los operadores linq estándar suelen diferirse y ToArray()
/ ToList()
siempre se materializan.
IQueryable
se usa cuando está utilizando Linq-to-entidades = está creando una consulta LINQ declarativa en su aplicación que el proveedor de LINQ interpretará como SQL y ejecutará en el servidor. Una vez que la consulta se ejecuta (iterada), se convertirá en IEnumerable y los objetos se materializarán según sea necesario para la iteración = no inmediatamente.
Una vez que llama al procedimiento almacenado, no está utilizando Linq-to-enters porque no hay una consulta declarativa incorporada en su aplicación. La consulta / SQL ya existe en el servidor de base de datos y solo lo está invocando. Esto devolverá IEnumerable
pero nuevamente no materializará todos los resultados inmediatamente. Los resultados se materializarán como iterados. Este es el principio del cursor de la base de datos / o del lector de datos .NET cuando se solicita explícitamente la obtención del objeto.
Así que si llamas a algo como esto:
foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
.Select(l => l.Value))
{
...
}
Usted está obteniendo cursos uno por uno (por cierto, ¿por qué cargar todo el curso si está interesado solo en palabras clave?). Hasta que complete o rompa el bucle, su lector de datos se abrirá para buscar registros.
Si en cambio llamas a esto:
foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
.ToList() // or ToArray
.Select(l => l.Value))
{
...
}
Forzará la consulta para materializar todos los resultados inmediatamente y el ciclo se ejecutará en la colección en la memoria en lugar de en el lector de la base de datos abierta.
La diferencia entre IQueryable
e IEnumerable
no está en la forma en que se obtienen los datos porque IEnumerable
es IQueryable
. La diferencia está en la construcción de respaldo (algo debe implementar estas interfaces).