¿Cuál es la forma correcta de implementación del repositorio en EF Core?
public IAsyncEnumerable<Order> GetOrder(int orderId) { return blablabla.AsAsyncEnumerable(); }o
public Task<IEnumerable<Order>> GetOrder(int orderId) { return blablabla.ToListAsync(); } ¿Es inteligente para el rendimiento llamar a AsAsyncEnumerable() ? ¿Es este enfoque seguro? Por un lado, no crea el objeto List<T> , por lo que debería ser un poco más rápido. Pero desde la mano de la orden no se materializa la consulta por lo que aplazamos la ejecución del SQL y el resultado puede cambiar mientras tanto.
Según la fuente , .ToListAsync usará IAsyncEnumerable internamente de todos modos, por lo que no hay muchos beneficios de rendimiento en uno u otro. Pero una característica importante de .ToListAsync o .ToArrayAsync es la cancelación .
public static async Task<List<TSource>> ToListAsync<TSource>( this IQueryable<TSource> source, CancellationToken cancellationToken = default) { var list = new List<TSource>(); await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken)) { list.Add(element); } return list; }La lista básicamente mantendrá todo en la memoria, pero podría ser un problema serio de rendimiento solo si la lista es realmente grande. En este caso, podría considerar paginar su gran respuesta.
public Task<List<Order>> GetOrders(int orderId, int offset, int limit) { return blablabla.Skip(offset).Take(limit).ToListAsync(); }La decisión realmente se reduce a si desea almacenar en búfer o transmitir.
Si desea almacenar en búfer los resultados, use ToList() o ToListAsync() .
Si desea transmitir los resultados, use AsEnumerable() o AsAsyncEnumerable() .
De los documentos :
El almacenamiento en búfer se refiere a cargar todos los resultados de su consulta en la memoria, mientras que la transmisión significa que EF entrega a la aplicación un solo resultado cada vez, sin contener nunca el conjunto de resultados completo en la memoria. En principio, los requisitos de memoria de una consulta de transmisión son fijos: son los mismos si la consulta devuelve 1 fila o 1000; una consulta de almacenamiento en búfer, por otro lado, requiere más memoria cuantas más filas se devuelvan. Para las consultas que dan como resultado grandes conjuntos de resultados, esto puede ser un factor de rendimiento importante.
En general, es mejor transmitir, a menos que necesite almacenar en búfer.
Cuando transmite, una vez que se leen los datos, no puede volver a leerlos sin volver a presionar la base de datos. Entonces, si necesita leer los mismos datos más de una vez, deberá almacenar en búfer.
Si un repositorio transmite un IEnumerable , la persona que llama puede optar por almacenarlo en búfer llamando a ToList() (o ToListAsync() en IAsyncEnumerable ). Perdemos esta flexibilidad si el repositorio elige devolver una IList.
Entonces, para responder a su pregunta, es mejor que transmita el resultado al repositorio. Y deje que la persona que llama decida si quiere almacenar en búfer.
Si el equipo que trabaja en el proyecto no se siente cómodo con la semántica de transmisión, o si la mayor parte del código ya se almacena en búfer, podría tener sentido añadir un sufijo a los métodos que transmiten con algo como AsStream (por ejemplo, GetOrdersAsStream() ) para que sepan que no deberían hacerlo. No lo enumere más de una vez.
Así que un repositorio podría tener:
async Task<List<Order>> GetOrders() => await GetOrdersAsStream.ToListAsync(); IAsyncEnumerable<Order> GetOrdersAsStream() => ...