我必须使用存储过程来访问数据,并使用从数据库引擎发送的消息(如
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();
强制连接刷新消息并触发剩余事件。然而,这是一个非常不幸的解决方法。
感谢您的帮助!
为什么你的
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
操作一起使用,只需将方法的签名从异步更改为同步即可。
扩展 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