1

Consider this simple SQL script:

PRINT N'Dropping CREATE_TABLE events from DatabaseLog table...'
DELETE FROM [dbo].[DatabaseLog] WHERE Event = N'CREATE_TABLE'
PRINT N'Dropping ALTER_TABLE events from DatabaseLog table...'
DELETE FROM [dbo].[DatabaseLog] WHERE Event = N'ALTER_TABLE'
PRINT N'Done!'

When run from SSMS, against AdventureWorks 2012, it gives this output:

Dropping CREATE_TABLE events from DatabaseLog table...

(70 row(s) affected)
Dropping ALTER_TABLE events from DatabaseLog table...

(117 row(s) affected)
Done!

How do I reproduce this in .NET, including the rows affected lines?

By hooking into the InfoMessage event on the SqlConnection, I get the output of the PRINT statements, but this doesn't include the rows affected lines. Ideally, I want to capture the output exactly as if it was run in SSMS.

Note: The script is user-supplied, so modifying the script to output the row counts manually is not an option.

2
  • what are you using to execute the commands? for the example you could have SqlCommand for each statement and then use ExectueNonQuery() method that returns rows affected Commented Jan 17, 2018 at 11:14
  • Currently, we're using ExecuteNonQuery() from SMO, in order to handle GO separators. Splitting the script into separate SqlCommands is not an option, because that doesn't work if the script uses variables. Commented Jan 17, 2018 at 13:42

2 Answers 2

1

Finally found the right event to get the individual statement counts from! StatementCompleted on the individual SqlCommand fires multiple times, once for each statement that affects rows. So:

public static void ExecuteScript(string connectionString, string sql)
{
    using (var conn = new SqlConnection(connectionString))
    {
        conn.InfoMessage += (sender, args) => Console.WriteLine(args.Message);
        conn.FireInfoMessageEventOnUserErrors = true;
        conn.Open();
        using (var cmd = new SqlCommand(sql, conn))
        {
            cmd.StatementCompleted += (sender, args) =>
                Console.WriteLine($"{Environment.NewLine}({args.RecordCount} row(s) affected)");
            cmd.ExecuteNonQuery();
        }
    }
}

reproduces the output from SSMS, for the queries I've tries so far, which have included CREATE TABLE, PRINT, INSERT, DELETE, and SELECT.

Setting FireInfoMessageEventOnUserErrors to true is necessary, otherwise all the InfoMessage events appear at the end, rather than being correctly interleaved with StatementCompleted events. However, when FireInfoMessageEventOnUserErrors is set to true, SqlException will not be thrown if there are errors, so you must check each InfoMessage for errors (if the Class is set to > 10).

Sign up to request clarification or add additional context in comments.

2 Comments

I found it too but you beat me to it :-) The secret sauce was FireInfoMessageEventOnUserErrors but note the onus is on you to identify errors rather than rely on try/catch.
Thanks, I would probably have missed that detail! I'll add it to the answer.
0

ExecuteNonQuery should return the affected rows;

SqlCommand comm = new SqlCommand("DELETE FROM [dbo].[DatabaseLog] WHERE Event = N'CREATE_TABLE'",conn);
int rowsAffected = comm.ExecuteNonQuery();

4 Comments

This only works if there is only a single statement. As I am not in control of the script, and indeed the script will usually contain multiple statements, this is not an option.
What value is returned if you have multiple statements. Did you try it ?
@Stormcloak, I would expect the cumulative row count for the entire batch to be returned.
Yes, it returns the total row count - 187, in the example I gave in the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.