Estoy trabajando en algunas cosas de la API web usando Entity Framework 6 y uno de mis métodos de controlador es un "Obtener todo" que espera recibir el contenido de una tabla de mi base de datos como IQueryable<Entity>
. En mi repositorio, me pregunto si hay alguna razón ventajosa para hacer esto de forma asincrónica, ya que soy nuevo en el uso de EF con async.
Básicamente se reduce a
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
¿La versión asíncrona realmente producirá beneficios de rendimiento aquí o estoy incurriendo en gastos generales innecesarios al proyectar primero a una Lista (usando asíncrono, claro) y LUEGO a IQueryable?
c#
entity-framework
async-await
Jesse Carter
fuente
fuente
Respuestas:
El problema parece ser que no ha entendido cómo funciona async / await con Entity Framework.
Acerca de Entity Framework
Entonces, veamos este código:
y ejemplo de su uso:
¿Qué pasa ahí?
IQueryable
objeto (aún no accedemos a la base de datos) usandorepo.GetAllUrls()
IQueryable
objeto con una condición especificada usando.Where(u => <condition>
IQueryable
objeto con límite de paginación especificado usando.Take(10)
.ToList()
. NuestroIQueryable
objeto se compila en sql (comoselect top 10 * from Urls where <condition>
). Y la base de datos puede usar índices, el servidor SQL le envía solo 10 objetos de su base de datos (no todos los mil millones de URL almacenados en la base de datos)Bien, veamos el primer código:
Con el mismo ejemplo de uso obtuvimos:
await context.Urls.ToListAsync();
.Acerca de async / await
¿Por qué se prefiere usar async / await? Veamos este código:
¿Qué pasa aquí?
var stuff1 = ...
userId
var stuff2 = ...
userId
Así que veamos una versión asincrónica:
¿Qué pasa aquí?
Manera correcta de hacerlo
Tan buen código aquí:
Tenga en cuenta que debe agregar
using System.Data.Entity
para usar el métodoToListAsync()
para IQueryable.Tenga en cuenta que si no necesita filtrado, paginación y demás, no necesita trabajar con
IQueryable
. Puede usarawait context.Urls.ToListAsync()
y trabajar con materializedList<Url>
.fuente
GetAllUrlsByUser
método, no es necesario que sea asíncrono. Simplemente devuelva la tarea y evite que el compilador genere una máquina de estado innecesaria.async
yawait
si NO está haciendo nada con la lista. Deje que la persona que llama lo hagaawait
. Cuando espera la llamada en esta etapareturn await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
, está creando un contenedor asíncrono adicional cuando descompila el ensamblado y mira el IL.Hay una gran diferencia en el ejemplo que ha publicado, la primera versión:
Esto es malo , básicamente lo hace
select * from table
, devuelve todos los resultados a la memoria y luego aplica los resultadoswhere
en la colección de memoria en lugar de hacerloselect * from table where...
en la base de datos.El segundo método no llegará a la base de datos hasta que se aplique una consulta a
IQueryable
(probablemente a través de una operación de.Where().Select()
estilo linq que solo devolverá los valores de la base de datos que coincidan con la consulta.Si sus ejemplos fueran comparables, la
async
versión generalmente será un poco más lenta por solicitud, ya que hay más sobrecarga en la máquina de estado que genera el compilador para permitir laasync
funcionalidad.Sin embargo, la principal diferencia (y beneficio) es que la
async
versión permite más solicitudes simultáneas, ya que no bloquea el hilo de procesamiento mientras espera que IO se complete (consulta de base de datos, acceso a archivos, solicitud web, etc.).fuente
En pocas palabras,
IQueryable
está diseñado para posponer el proceso RUN y, en primer lugar, construir la expresión junto con otrasIQueryable
expresiones, y luego interpretar y ejecutar la expresión como un todo.Pero el
ToList()
método (o algunos tipos de métodos como ese), está indicado para ejecutar la expresión instantáneamente "tal cual".Su primer método (
GetAllUrlsAsync
), se ejecutará inmediatamente, porque esIQueryable
seguido porToListAsync()
método. por lo tanto, se ejecuta instantáneamente (asincrónico) y devuelve un montón deIEnumerable
s.Mientras tanto, su segundo método (
GetAllUrls
), no se ejecutará. En cambio, devuelve una expresión y CALLER de este método es responsable de ejecutar la expresión.fuente