Saltar al contenido

¿Qué sucede con la devolución de IEnumerable si se usa con async/await (transmisión de datos desde SQL Server con Dapper)?

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:

  1. agregar una nueva API para .NET Core solamentedevolviendo un tipo enumerable asincrónico apropiado
  2. 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:

  1. limitar la función a .NET Core 3.0 y devolver IAsyncEnumerable
  2. limitar el biblioteca a .NET Core 3.0 y volver IAsyncEnumerable
  3. 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
  4. devolver un tipo enumerable personalizado que no es Realmente IAsyncEnumerable (pero que implementa IAsyncEnumerable cuando esté disponible), e implemente manualmente la máquina de estado: la naturaleza tipo pato de foreach 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.

Te mostramos las comentarios y valoraciones de los usuarios

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *