/// <summary> /// Executes this batch and captures any server messages that are returned. /// </summary> /// <param name="conn">The connection to use to execute the batch</param> /// <param name="cancellationToken">Token for cancelling the execution</param> public async Task Execute(DbConnection conn, CancellationToken cancellationToken) { // Sanity check to make sure we haven't already run this batch if (HasExecuted) { throw new InvalidOperationException("Batch has already executed."); } // Notify that we've started execution if (BatchStart != null) { await BatchStart(this); } try { // Register the message listener to *this instance* of the batch // Note: This is being done to associate messages with batches ReliableSqlConnection sqlConn = conn as ReliableSqlConnection; DbCommand command; if (sqlConn != null) { // Register the message listener to *this instance* of the batch // Note: This is being done to associate messages with batches sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler; command = sqlConn.GetUnderlyingConnection().CreateCommand(); // Add a handler for when the command completes SqlCommand sqlCommand = (SqlCommand)command; sqlCommand.StatementCompleted += StatementCompletedHandler; } else { command = conn.CreateCommand(); } // Make sure we aren't using a ReliableCommad since we do not want automatic retry Debug.Assert(!(command is ReliableSqlConnection.ReliableSqlCommand), "ReliableSqlCommand command should not be used to execute queries"); // Create a command that we'll use for executing the query using (command) { command.CommandText = BatchText; command.CommandType = CommandType.Text; command.CommandTimeout = 0; executionStartTime = DateTime.Now; // Execute the command to get back a reader using (DbDataReader reader = await command.ExecuteReaderAsync(cancellationToken)) { int resultSetOrdinal = 0; do { // Skip this result set if there aren't any rows (ie, UPDATE/DELETE/etc queries) if (!reader.HasRows && reader.FieldCount == 0) { continue; } // This resultset has results (ie, SELECT/etc queries) ResultSet resultSet = new ResultSet(reader, resultSetOrdinal, Id, outputFileFactory); resultSet.ResultCompletion += ResultSetCompletion; // Add the result set to the results of the query lock (resultSets) { resultSets.Add(resultSet); resultSetOrdinal++; } // Read until we hit the end of the result set await resultSet.ReadResultToEnd(cancellationToken).ConfigureAwait(false); } while (await reader.NextResultAsync(cancellationToken)); // If there were no messages, for whatever reason (NO COUNT set, messages // were emitted, records returned), output a "successful" message if (!messagesSent) { await SendMessage(SR.QueryServiceCompletedSuccessfully, false); } } } } catch (DbException dbe) { HasError = true; await UnwrapDbException(dbe); } catch (TaskCanceledException) { // Cancellation isn't considered an error condition await SendMessage(SR.QueryServiceQueryCancelled, false); throw; } catch (Exception e) { HasError = true; await SendMessage(SR.QueryServiceQueryFailed(e.Message), true); throw; } finally { // Remove the message event handler from the connection ReliableSqlConnection sqlConn = conn as ReliableSqlConnection; if (sqlConn != null) { sqlConn.GetUnderlyingConnection().InfoMessage -= ServerMessageHandler; } // Mark that we have executed HasExecuted = true; executionEndTime = DateTime.Now; // Fire an event to signify that the batch has completed if (BatchCompletion != null) { await BatchCompletion(this); } } }