SqlCommand.StatementCompleted 应该何时触发?
我正在尝试编写一个简单的 winforms 应用程序来异步执行 SQL SELECT 语句。当 sql 服务器开始返回结果时,我想执行一个已连接到 SqlCommand 的 StatementCompleted 事件的事件处理程序。
该表单包含两个按钮、一个文本框和一个标签。单击button1 时,我创建SqlCommand 并连接事件处理程序,然后打开SqlConnection 并调用BeginExecuteReader 以启动异步操作。我设置标签以显示命令正在执行。
在事件处理程序中,我只需设置标签即可显示命令已完成。
单击按钮 2 时,我更改标签以显示我们正在处理结果。然后,我调用 EndExecuteReader 并将其返回值分配给一个新的 SqlDataReader,然后对其进行处理。
我看到的是,当命令准备好时,事件处理程序不会被调用。相反,当我的代码完成处理 EndExecuteReader 返回的读取器时,它会被调用。
我在这里错过了什么吗?我是否误解了该活动的预期用途?我试图找到 StatementCompleted 的示例,但我只能找到它的一般描述,没有工作代码。 MSDN 上的 SqlCommand.BeginExecuteReader 页面中的示例< /a> 使用循环并等待 IAsyncResult.IsCompleted 属性为 true。我希望在该属性变为 true 的同时,将触发 StatementCompleted 事件。
public Form1() {
InitializeComponent();
}
private IAsyncResult iAsyncResult;
private SqlCommand sqlCommand;
private void statementCompleted(object sender,
StatementCompletedEventArgs e) {
label1.Text = "Statement completed";
}
private void button1_Click(object sender, EventArgs e) {
var northWindConnection =
new SqlConnection(
"Data Source=.\\SqlExpress;Initial Catalog=Northwind;" +
"Integrated Security=True;" +
"asynchronous processing=true");
sqlCommand = new SqlCommand("WAITFOR DELAY '00:00:05';" +
" SELECT * FROM [Order Details]",
northWindConnection);
sqlCommand.StatementCompleted += statementCompleted;
northWindConnection.Open();
iAsyncResult = sqlCommand.BeginExecuteReader();
label1.Text = "Executing";
}
private void button2_Click(object sender, EventArgs e) {
label1.Text = "Not waiting anymore, reading";
var results = new StringBuilder();
var reader = sqlCommand.EndExecuteReader(iAsyncResult);
while (reader.Read()) {
for (int i = 0; i < reader.FieldCount; i++) {
results.Append(reader[i].ToString() + "\t");
}
results.Append(Environment.NewLine);
}
reader.Close();
sqlCommand.Connection.Close();
textBox1.Text = results.ToString();
}
I'm trying to write a simple winforms application that executes a SQL SELECT statement asynchronous. When the sql server starts returning results, I want to execute an event handler I've wired up to the SqlCommand's StatementCompleted event.
The form contains two buttons, a textbox, and a label. When button1 is clicked, I create the SqlCommand and wire up the event handler, then I open the SqlConnection and call BeginExecuteReader in order to start the asynchronous operation. I set my label to show the command is executing.
In the event handler, I simply set the label to show the command is finished.
When button 2 is clicked, I change the label to show we're processing the results. Then I call EndExecuteReader and assign its return value to a new SqlDataReader which I then process.
What I see is that the event handler doesn't get called when the command is ready. In stead, it gets called when my code finishes processing the reader returned by EndExecuteReader.
Am I missing something here? Do I misinterpret the intended use of the event? I've tried to find an example of StatementCompleted, but I could only find general descriptions of it, no working code. The example at the SqlCommand.BeginExecuteReader page at MSDN uses a loop and waits for the IAsyncResult.IsCompleted property to be true. I would expect that at the same time that property gets true, the StatementCompleted event fires.
public Form1() {
InitializeComponent();
}
private IAsyncResult iAsyncResult;
private SqlCommand sqlCommand;
private void statementCompleted(object sender,
StatementCompletedEventArgs e) {
label1.Text = "Statement completed";
}
private void button1_Click(object sender, EventArgs e) {
var northWindConnection =
new SqlConnection(
"Data Source=.\\SqlExpress;Initial Catalog=Northwind;" +
"Integrated Security=True;" +
"asynchronous processing=true");
sqlCommand = new SqlCommand("WAITFOR DELAY '00:00:05';" +
" SELECT * FROM [Order Details]",
northWindConnection);
sqlCommand.StatementCompleted += statementCompleted;
northWindConnection.Open();
iAsyncResult = sqlCommand.BeginExecuteReader();
label1.Text = "Executing";
}
private void button2_Click(object sender, EventArgs e) {
label1.Text = "Not waiting anymore, reading";
var results = new StringBuilder();
var reader = sqlCommand.EndExecuteReader(iAsyncResult);
while (reader.Read()) {
for (int i = 0; i < reader.FieldCount; i++) {
results.Append(reader[i].ToString() + "\t");
}
results.Append(Environment.NewLine);
}
reader.Close();
sqlCommand.Connection.Close();
textBox1.Text = results.ToString();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
事件顺序如下:
SqlCommand.BeginExecuteReader(callback, stateObject)
将 T-SQL 发送到 SQL Server,然后命令开始执行。BeginExecuteReader()
的AsyncCallback
。EndExecuteReader()
来获取对SqlDataReader
对象的引用。SqlDataReader
读取查询结果。这可能是一行,也可能是数百万行。 在返回所有请求的数据之前,查询尚未完成。StatementCompleted
事件 - 但前提是查询/存储过程未使用SET NOCOUNT ON
。换句话说,当 T-SQL 完全完成(包括所有关联的数据传输)时,将调用
StatementCompleted
。The sequence of events is this:
SqlCommand.BeginExecuteReader(callback, stateObject)
sends the T-SQL to SQL Server and the command starts executing.AsyncCallback
provided toBeginExecuteReader()
is called.EndExecuteReader()
to obtain a reference to aSqlDataReader
object.SqlDataReader
to read the results of the query. This could be one row, or millions of rows. The query is not complete until all data requested has been returned.StatementCompleted
event -- but only if the query / stored procedure did not useSET NOCOUNT ON
.In other words,
StatementCompleted
is called when the T-SQL has completely finished, including all associated data transfers.为可能遇到此问题的任何人添加此内容,因为几个月前就有人提出这个问题,但没有提供答案。
StatementCompleted 事件在针对 SqlCommand 应用异步调用模式时没有用处。它确实被触发,但仅在调用 EndExecuteReader 期间被触发,这基本上为时已晚。如果您想实现一般的异步调用模式,这篇 MSDN 文章 很好地解释了如何做到这一点。 BeginExecuteReader 文档中的示例代码显示了 SqlCommand 在异步模式。
Adding this for anyone that might run across this question since it was asked months ago with no answers provided.
The StatementCompleted event isn't useful in applying an async call pattern against SqlCommand. It does get fired but only during the call to EndExecuteReader which is basically too late. If you want to implement an async call pattern in general, this MSDN article has an excellent explanation of how it can be done. The sample code in the BeginExecuteReader documentation shows the correct usage of SqlCommand in an async mode.
我怀疑此行为的线索是事件的“StatementCompletedEventArgs”参数包含属性“RecordCount”,它是受语句影响的行数。
MS SqlServer(以及在此之前的 Sybase SqlServer,就像当时一样)在实际数据全部发送后,将受影响的行数作为单独的“消息”(宽松地使用术语)返回。
另外,请注意:一个 Sql 命令可以由多个 SQL 语句组成,每个 SQL 语句可以影响多个行,因此返回多个“受影响的行”。因此,我假设对于给定的 SQL 命令,该事件可能会触发多次;或者根本没有使用 SET NOCOUNT ON 次数。
I suspect the clue to this behaviour is that the event's "StatementCompletedEventArgs" parameter includes the property "RecordCount" which is the number of rows affected by a statement.
MS SqlServer (and before that Sybase SqlServer, as it then was) returns the number of rows affected as a separate "message" (using the term loosely) after the actual data has all been sent.
Also, beware: A Sql Command can consist of a number of SQL Statements, each of which can affect a number of rows and therefore return a number of "rows affected". I would therefore assume the event might fire several times for a given SQL Command; or no times at all is SET NOCOUNT ON was used.
对于遇到此问题的其他人,您的语句必须返回结果集才能实例化 SqlDataReader。如果没有结果集,则永远不会触发 StatementCompleted 事件。
我在尝试恢复时遇到了这个问题。我最终只是在查询末尾添加了 SELECT 1 ,这解决了问题。
For anyone else that was running into an issue with this, your statement has to return a resultset in order to instantiate the SqlDataReader. If there is no resultset, then the StatementCompleted event never fires.
I ran into this when trying to do a restore. I ended up just adding a SELECT 1 at the end of my query and that fixed the issue.