24

Is it possible to access the SQL Server "by-product messages" via ADO.NET? Due to the lack of words, by "by-product messages" I mean the output which appears in the Messages tab in Microsoft SQL Server Management Studio. What I particularly have it mind is to read the output of SET STATISTICS TIME ON. It appears that SqlDataReader does not offer anything in this matter.

Jan Zich
  • 14,993
  • 18
  • 61
  • 73

3 Answers3

31

Yes, there's an event on the SqlConnection class called SqlInfoMessage, which you can hook into:

SqlConnection _con = 
   new SqlConnection("server=.;database=Northwind;integrated Security=SSPI;");

_con.InfoMessage += new SqlInfoMessageEventHandler(InfoMessageHandler);

The event handler will look like this:

static void InfoMessageHandler(object sender, SqlInfoMessageEventArgs e)
{
    string myMsg = e.Message;            
}

The e.Message is the message printed out to the message window in SQL Server Management Studio.

Joseph Daigle
  • 47,650
  • 10
  • 49
  • 73
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
5

Thank you for the response above. I just did a little experiment and found a little unexpected glitch (a bug?) when reading messages (in this case produced by SET STATISTICS TIME ON) from a multi-recordset result. As indicated below, one has to call NextResult even after the last resultset in order to get the last message. This is not needed in the case of a single recordset result.

using System;
using System.Data.SqlClient;

namespace TimingTest
{
    class Program
    {

        static void Main(string[] args)
        {

            SqlConnection conn = new SqlConnection("some_conn_str");
            conn.Open();

            conn.InfoMessage += new SqlInfoMessageEventHandler(Message);

            SqlCommand cmd = new SqlCommand("some_sp", conn);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            SqlDataReader rdr = cmd.ExecuteReader();

            while (rdr.Read()) { };

            rdr.NextResult();

            while (rdr.Read()) { };

            // this is needed to print the second message
            rdr.NextResult();

            rdr.Close();

            conn.Close();

        }

        static void Message(object sender, SqlInfoMessageEventArgs e)
        {
            Console.Out.WriteLine(e.Message);
        }

    }
}
Jan Zich
  • 14,993
  • 18
  • 61
  • 73
  • 1
    This would be great as an answer to a separate question, e.g. "How do I get the last info message after executing a query that returns multiple result-sets?" or something similar. – Kenny Evitt Jan 22 '12 at 06:32
  • As a comment on the code for anyone not aware, the empty while loops can also be written like: `while (rdr.Read()) ;` – Samuel Slade Mar 28 '14 at 09:27
  • Jan, thanks for this little piece of gold dust that one has to sprinkle on to make things work. Kudos! This had me stumped for quite a while, and without your write-up I'd probably still be looking for the solution ... – DarthGizka May 07 '23 at 08:00
1

Based on marc_s' answer, I ve created a wrapper class

public class SqlInfoMessageWrapper
{
     public SqlInfoMessageWrapper(SqlConnection connection)
     {
            SqlConnection = connection;
            connection.InfoMessage += new SqlInfoMessageEventHandler(InfoMessageHandler);
      }
      public SqlConnection SqlConnection { get; set; }
      public string Message  { get; set; }

      void InfoMessageHandler(object sender, SqlInfoMessageEventArgs e)
      {
            Message = e.Message;
      }
 }

Example of use :

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        var messageWrapper=new SqlInfoMessageWrapper(connection) ;

        var ret = SqlHelper2.ExecuteNonQuery(connection, CommandType.Text, command, null);
        messages+= $"{messageWrapper.Message} number of rows affected {ret} ";
    }
Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170