Sé libre de compartir nuestra página y códigos con otro, necesitamos de tu ayuda para ampliar nuestra comunidad.
Solución:
Actualización de marzo de 2020
.NET Core 3.0 (y 3.1) han salido ahora, con soporte completo para flujos asíncronos. Microsoft.Bcl.AsyncInterfaces agrega soporte para ellos a .NET Standard 2.0 y .NET Framework 4.6.1+, aunque 4.7.2 debe usarse por razones de cordura. Como explican los documentos sobre el soporte de implementación de .NET Standard
Si bien NuGet considera que .NET Framework 4.6.1 es compatible con .NET Standard 1.5 a 2.0, existen varios problemas con el consumo de bibliotecas de .NET Standard que se crearon para esas versiones de proyectos de .NET Framework 4.6.1.
Para los proyectos de .NET Framework que necesitan usar dichas bibliotecas, le recomendamos que actualice el proyecto para tener como destino .NET Framework 4.7.2 o superior.
Respuesta original
Si revisa el código fuente, verá que su sospecha es casi correcta. Cuando buffered
es false, QueryAsync
transmitirá sincrónicamente.
if (command.Buffered)
var buffer = new List();
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) /* ignore subsequent result sets */
command.OnCompleted();
return buffer;
else
// can't use ReadAsync / cancellation; but this will have to do
wasClosed = false; // don't close if handing back an open reader; rely on the command-behavior
var deferred = ExecuteReaderSync(reader, func, command.Parameters);
reader = null; // to prevent it being disposed before the caller gets to see it
return deferred;
Como explica el comentario, no es posible usar ReadAsync
cuando se espera que el tipo de valor devuelto sea IEnumerable. Es por eso que se tuvieron que introducir los enumerables asíncronos de C# 8.
El código para ExecuteReaderSync es:
private static IEnumerable ExecuteReaderSync(IDataReader reader, Func func, object parameters)
using (reader)
while (reader.Read())
yield return (T)func(reader);
while (reader.NextResult()) /* ignore subsequent result sets */
(parameters as IParameterCallbacks)?.OnCompleted();
Usa Read
en vez de ReadAsync
.
Las secuencias asíncronas de C#8 permitirán reescribir esto para devolver un IAsyncEnumerable
. Simplemente cambiar la versión del idioma no resolverá el problema.
Dados los documentos actuales sobre transmisiones asíncronas, esto podría verse así:
private static async IAsyncEnumerable ExecuteReaderASync(IDataReader reader, Func func, object parameters)
using (reader)
while (await reader.ReadAsync())
yield return (T)func(reader);
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) /* ignore subsequent result sets */
command.OnCompleted();
(parameters as IParameterCallbacks)?.OnCompleted();
Peeeero los flujos asíncronos son una de las cosas que solo pueden funcionar en .NET Core, y probablemente aún no estén implementadas. Cuando traté de escribir uno en Sharplab.io, Kaboom. [connection lost, reconnecting…]
En el contexto de dapper específicamente, sí: necesita una API diferente como lo explica la excelente respuesta de @Panagiotis. Lo que sigue no es un responder como tal, pero es un contexto adicional que los implementadores que enfrentan los mismos desafíos pueden considerar.
Todavía no he “aplicado” esto para que sea elegante (aunque tener para SE.Redis), y estoy dividido entre varias opciones:
- agregar una nueva API para .NET Core solamentedevolviendo un tipo enumerable asincrónico apropiado
- aplastar por completo la API existente como un cambio importante (un “principal”, etc.), cambiándola para devolver un tipo enumerable asíncrono
Probablemente elegiremos “1”, pero debo decir que la segunda opción es inusualmente tentadora, por buenas razones:
- la API existente probablemente no hace lo que la gente espera que haga
- nos gustaría un nuevo código para comenzar a usarlo
Pero lo extraño es el .NET Core 3.0-ness de IAsyncEnumerable
– como obviamente, Dapper no solo apunta a .NET Core 3.0; Pudimos:
- limitar la función a .NET Core 3.0 y devolver
IAsyncEnumerable
- limitar el biblioteca a .NET Core 3.0 y volver
IAsyncEnumerable
- tome una dependencia de System.Linq.Async (que no es “oficial”, pero es lo suficientemente oficial para nuestros propósitos) para los marcos anteriores, y regrese
IAsyncEnumerable
- devolver un tipo enumerable personalizado que no es Realmente
IAsyncEnumerable
(pero que implementaIAsyncEnumerable
cuando esté disponible), e implemente manualmente la máquina de estado: la naturaleza tipo pato deforeach
significa que esto funcionará bien siempre que nuestro tipo enumerable personalizado proporcione los métodos correctos
creo que lo haremos probablemente vaya con la opción 3, pero para reiterar: sí, algo debe cambiar.
(Se supone que esto es un comentario // no hay suficiente reputación, hasta ahora)
Marc Gravell menciona en su respuesta que IAsyncEnumerable
sería preferible, pero debido a la dependencia de NET Core 3.0, podría ser mejor depender de System.Linq.Async
(que podría considerarse como “suficientemente oficial”)…
En este contexto, me vino a la mente https://github.com/Dasync/AsyncEnumerable (licencia MIT): tiene como objetivo ayudar
… para (a) crear un proveedor de elementos, donde la producción de un elemento puede llevar mucho tiempo debido a la dependencia de otros eventos asincrónicos (por ejemplo, identificadores de espera, flujos de red), y (b) un consumidor que procesa esos elementos tan pronto ya que están listos sin bloquear el subproceso (en su lugar, el procesamiento se programa en un subproceso de trabajo).
Una pregunta más, RE: “¿Qué sucede cuando se lanza C# 8.0?” (PREGUNTAS MÁS FRECUENTES)
C# 8.0 debería tener una característica de Async Streams. Cuando finalmente se lance la versión del idioma, debería ser una ruta de actualización directa para su aplicación.