SqlCommand.ExecuteReaderAsync 在存储过程中的第一个 SELECT 语句后停止触发 InfoMessage 事件

问题描述 投票:0回答:2

我必须使用存储过程来访问数据,并使用从数据库引擎发送的消息(如

PRINT 'hello'
)。

当我使用SQL连接的

InfoMessage
事件并将数据填充到
DataTable
时,一切都很完美。

但是,当我必须顺序读取数据并使用

SqlDataReader.ExecuteReaderAsync
时,连接会在存储过程中的第一个 select 语句之后停止触发 InfoMessage:

C#代码:

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();
强制连接刷新消息并触发剩余事件。然而,这是一个非常不幸的解决方法。

感谢您的帮助!

c# sql stored-procedures ado.net executereader
2个回答
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
操作一起使用,只需将方法的签名从异步更改为同步即可。


0
投票

扩展 vikscool 答案,如果您像我一样使用

SqlDataAdapter
,您可以简单地在 SP 末尾添加最简单的选择,并确保使用
DataSet
作为
SqlDataAdapter.Fill()
的目标。这将使 SqlDataAdapter 处理第一个结果集之后的消息。填充
DataTable
时,适配器会在第一个结果集后停止触发
InfoMessage
事件。 SQL 示例:

print 'first message'
select 1 as result1
print 'second message'
select 'this is the end' as end
© www.soinside.com 2019 - 2024. All rights reserved.