SQL Server When is SqlCommand.StatementCompleted supposed to fire?

kpbwa7wx  于 2023-10-15  发布在  其他
关注(0)|答案(4)|浏览(81)

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();
}
hvvq6cgz

hvvq6cgz1#

The sequence of events is this:

  1. Call SqlCommand.BeginExecuteReader(callback, stateObject) sends the T-SQL to SQL Server and the command starts executing.
  2. When data is first available, the AsyncCallback provided to BeginExecuteReader() is called.
  3. The callback invokes EndExecuteReader() to obtain a reference to a SqlDataReader object.
  4. You use the 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.
  5. Repeat for additional result sets, if any.
  6. Invoke the StatementCompleted event -- but only if the query / stored procedure did not use SET NOCOUNT ON .

In other words, StatementCompleted is called when the T-SQL has completely finished, including all associated data transfers.

yrwegjxp

yrwegjxp2#

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.

yshpjwxd

yshpjwxd3#

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.

pdtvr36n

pdtvr36n4#

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.

相关问题