¿Cómo hago que SqlDataReader.ReadAsync () se ejecute de forma asincrónica?

Al hacer llamadas a SQL Server que realmente hacen cosas que llevan tiempo,SqlDataReader.ReadAsync() corre sincrónicamente para mí ¿Hay alguna forma de obligarlo a ejecutarse de forma asincrónica o es mi única opción para llamarlo?Task.Run()?

Aquí hay una repro. Utiliza winforms para demostrar que la llamada bloquea el hilo GUI. Tenga en cuenta que el T-SQL realmente tiene que hacer algo:esto no es reproducible conWAITFOR DELAY '00:00:20'.

using System;
using System.Configuration;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Windows.Forms;

static class SqlDataReaderReadAsyncProgram
{
    static async void Form_Shown(object sender, EventArgs e)
    {
        var form = (Form)sender;
        // Declare your connection string in app.config like
        // <connectionStrings><remove name="LocalSqlServer"/><add name="LocalSqlServer" connectionString="Data Source=localhost\SQLEXPRESS;Integrated Security=true"/></connectionStrings>
        using (DbConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))
        {
            form.Text = "connecting…";
            await connection.OpenAsync();
            form.Text = "connected!";
            // Install a stored procedure.
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "SET NOCOUNT ON"
                    + " SELECT 'a'"
                    + " DECLARE @t DATETIME = SYSDATETIME()"
                    + " WHILE DATEDIFF(s, @t, SYSDATETIME()) < 20 BEGIN"
                    + "   SELECT 2 x INTO #y"
                    + "   DROP TABLE #y"
                    + " END"
                    + " SELECT 'b'";
                form.Text = "executing…";
                using (var reader = await command.ExecuteReaderAsync())
                {
                    form.Text = "reading…";
                    do
                    {
                        // Blocks on the second call until the second resultset is returned by SQL Server
                        while (await reader.ReadAsync())
                        {
                        }
                    } while (await reader.NextResultAsync());
                    form.Text = "done!";
                }
            }
        }
        await Task.Delay(TimeSpan.FromSeconds(5));
        form.Close();
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var form = new Form();
        form.Shown += Form_Shown;
        Application.Run(form);
    }
}

Cuando ejecuto esto, la ventana se convierte en "(No responde)" durante 20 segundos antes de informar que se hace (tenga en cuenta que al depurar en VS, el texto "(No responde)" no aparece pero aún se congela). Si depuro VS y lo rompo mientras está congelado, lo veo sentado con una pila de llamadas que se ve así:

    [Managed to Native Transition]  
    System.Data.dll!SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle pConn, ref System.IntPtr packet, int timeout) Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadByteArray(byte[] buff, int offset, int len, out int totalRead)    Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadInt64(out long value) Unknown
    System.Data.dll!System.Data.SqlClient.TdsParser.TryProcessDone(System.Data.SqlClient.SqlCommand cmd, System.Data.SqlClient.SqlDataReader reader, ref System.Data.SqlClient.RunBehavior run, System.Data.SqlClient.TdsParserStateObject stateObj)    Unknown
    System.Data.dll!System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior runBehavior, System.Data.SqlClient.SqlCommand cmdHandler, System.Data.SqlClient.SqlDataReader dataStream, System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler, System.Data.SqlClient.TdsParserStateObject stateObj, out bool dataReady)  Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.TryHasMoreRows(out bool moreRows)   Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.TryReadInternal(bool setTimeout, out bool more) Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.ReadAsync.AnonymousMethod__0(System.Threading.Tasks.Task t) Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.InvokeRetryable<bool>(System.Func<System.Threading.Tasks.Task, System.Threading.Tasks.Task<bool>> moreFunc, System.Threading.Tasks.TaskCompletionSource<bool> source, System.IDisposable objectToDispose)   Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.ReadAsync(System.Threading.CancellationToken cancellationToken) Unknown
    System.Data.dll!System.Data.Common.DbDataReader.ReadAsync() Unknown
>   SqlDataReaderReadAsync.exe!SqlDataReaderReadAsyncProgram.Form_Shown(object sender, System.EventArgs e) Line 36  C#
    [Resuming Async Method] 

(recortado aún más por brevedad).

El conjuntoReadSyncOverAsync Las cosas me parecen particularmente sospechosas. Es como si SqlClient asumiera que una lectura síncrona no se bloqueará, como si no supiera cómo usar E / S sin bloqueo o algo así. Sin embargo, al ver la fuente de referencia o descompilar con JustDecompile, parece que se supone que hay soporte asíncrono, pero de alguna manera heurístico / alternativo decidió no usarlo.

Entonces, ¿cómo obtengo el *Async() cosas en SqlClient para ser realmente asíncrono? Pensé que se suponía que estos métodos me permitirían escribir programas GUI receptivos sin hilos sin necesidad de usarTask.Run() porque envolviendo cosas sincrónicas enTask.Run() solo para hacerlos asíncronos no tiene sentido sobrecarga ...

Estoy usando .net-4.7.02542.

Supongo que se trata de un error .net y he enviadoconectar # 3139210 (EDITAR: conectar está muerto, tengo un proyecto de repro enhttps://github.com/binki/connect3139210)

ACTUALIZAR: Microsoft reconoce el error y lo arreglará en .net-4.7.3.Utilicé un caso de "Soporte técnico" de una suscripción de VS para informar el error y obtener esta información.

Respuestas a la pregunta(0)

Su respuesta a la pregunta