SqlCommand.ExecuteReaderAsync прекращает запускать событие InfoMessage после первого оператора SELECT в хранимой процедуре

Я должен использовать хранимые процедуры для доступа к данным, а также использовать сообщения (например, PRINT 'hello'), отправленные из механизма БД.

Когда я использую InfoMessage событие SQL-соединения и заполняю данные в DataTable, все работает отлично.

Однако, когда мне нужно последовательно читать данные и использовать SqlDataReader.ExecuteReaderAsync, соединение перестает запускать InfoMessage после первого оператора выбора в хранимой процедуре:

Код С#:

using (SqlConnection con = new SqlConnection("connection-string"))
{
    con.Open();
    con.InfoMessage += (s, e) => {
        Console.WriteLine(e.Message);
    };

    using (SqlCommand command = new SqlCommand("spTestMessage", con))
    {
        command.CommandType = System.Data.CommandType.StoredProcedure;

        SqlDataReader reader = await command.ExecuteReaderAsync();

        int cntr = 0;

        while (reader.Read())
        {
            Console.WriteLine($"Loaded row {cntr++}");
        }

        // reader.NextResult(); // this line forces firing rest of InfoMessage events
    }
}

Хранимая процедура SQL:

CREATE PROCEDURE [dbo].[spTestMessage]
AS

PRINT 'Before select 1'
select * from MyTable
PRINT 'After select 1'

PRINT 'Before select 2'
select * from MyTable
PRINT 'After select 2'

Вывод программы:

Before select 1

Почему он перестает работать? Я думаю, что с Reader что-то не так, потому что когда я использую command.ExecuteNonQueryAsync(); вместо command.ExecuteReaderAsync();, он тоже работает.

Я случайно узнал, что закомментированная строка reader.NextResult(); заставляет соединение сбрасывать сообщения и запускать оставшиеся события. Тем не менее, это очень неудачный обходной путь.

Спасибо за любую помощь!


person Tom    schedule 10.05.2019    source источник
comment
попробуйте while(await reader.ReadAsync()) вместо while (reader.Read()).   -  person vikscool    schedule 10.05.2019
comment
@vikscool Никакого эффекта.   -  person Tom    schedule 10.05.2019
comment
Попробуйте OpenAsync() в соединении и ReadAsync(). Кажется странным, что вы выполняете асинхронное чтение, но не читаете асинхронно.   -  person William Xifaras    schedule 10.05.2019


Ответы (1)


Причина, по которой ваш command.ExecuteNonQueryAsync() перестает выдавать ваш результат после первого запуска, с reader.ReadAsync() или без него, заключается в том, что любая асинхронная команда SQL будет завершена, как только первый результат будет возвращен клиенту, а info messages действительно считается результатом. И когда вы запускаете reader.NextResult() or await reader.NextResultAsync(), он проверяет дальнейшие результаты с помощью считывателя.

Если вы хотите узнать больше об обработке Asynchronous SQL, вы можете проверить ответ Remus, а для NextResult ознакомьтесь с этим пример.

Потребление InfoMessages:

var _msgs = new List<string>();
using (System.Data.SqlClient.SqlConnection con = new System.Data.SqlClient.SqlConnection(<YourConnectionString>))
{
    //Appending an event handler for InfoMessages
    con.InfoMessage += delegate (object sender, SqlInfoMessageEventArgs args)
    {
        //reader will invoke the delegate on every itteration of results coming from query
        _msgs.Add(args.Message);
        return;
    };
    using (var cmd = new System.Data.SqlClient.SqlCommand("spTestMessage", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        con.Open();
        using (System.Data.SqlClient.SqlDataReader reader = await cmd.ExecuteReaderAsync())
        {
            var incr = 0;
            while (await reader.ReadAsync())
            {
                //Statements to read data from Table1
                Console.WriteLine(reader.GetString(0));
                incr++;
            }
            while (await reader.NextResultAsync())
            {
                while (await reader.ReadAsync())
                {
                     //Statements to read data from Table2
                     Console.WriteLine(reader.GetString(0));
                     incr++;
                 }
             }
         }
     } 
}

Примечание. Описанную выше процедуру можно использовать и с Synchronous операциями, просто измените сигнатуру методов с асинхронной на синхронную.

person vikscool    schedule 13.05.2019
comment
Хорошо, большое спасибо. Я прочитал все упомянутые статьи, но до сих пор не понимаю, как лучше всего запускать асинхронный ридер и использовать все информационные сообщения. Потребляет ли reader.NextResult(); ресурсы и может ли вызвать проблемы с производительностью? - person Tom; 13.05.2019
comment
reader.NextResult() — это синхронный подход, тогда как reader.NextResultAsync() — асинхронный. Я рекомендовал только то, что вы делали с command.ExecuteReaderAsync(), и лучше иметь симметрию функций Async вместо смешанной подход. Что касается производительности, да, функция Async будет вам полезна. - person vikscool; 14.05.2019
comment
Конечно, я буду использовать асинхронную версию и не буду смешивать подходы. Вопросы таковы: много ли NextResult/NextResultAsync потребляет ресурсов и есть ли другой способ, как потреблять все информационные сообщения? - person Tom; 15.05.2019
comment
@Tom на ваш 1-й вопрос: нет, это не будет потреблять много ресурсов, но опять же это будет зависеть от того, как вы спроектируете свой процесс, но если вы беспокоитесь, что это может произойти, вы можете изменить подпись процесса на sync . Что касается вашего второго вопроса: я обновил свой ответ о том, как я буду использовать info messages(операторы печати с SQL-сервера). - person vikscool; 16.05.2019