Search code examples
c#sql-serversql-server-2014

Capturing RAISERROR in C#


I tried to find a useful answer to this question, but failed (the closest I found was this). I have a C# app calling a stored procedure which uses SQL TRY/CATCH to handle errors. I can replicate the issue with a sample stored procedure like this:

if object_id('dbo.TestSQLError') is not null drop proc dbo.TestSQLError
go
create proc dbo.TestSQLError
as

begin try
    select 1 / 0
end try
begin catch
    raiserror('Bad tings appen mon', 16, 1)
end catch

then a little dummy program like this:

namespace TestSQLError
{
    class Program
    {
        public const string CONNECTION_STRING = @"data source=localhost\koala; initial catalog=test; integrated security=true;";

        static void Main(string[] args)
        {
            try
            {
                using (SqlConnection conn = new SqlConnection(CONNECTION_STRING))
                {
                    SqlCommand cmd = new SqlCommand("dbo.TestSQLError", conn) { CommandType = System.Data.CommandType.StoredProcedure };
                    conn.Open();

                    using (SqlDataReader rdr = cmd.ExecuteReader())
                    {
                        while (rdr.Read())
                        {
                            Console.WriteLine(rdr.GetValue(0).ToString());
                        }
                        rdr.Close();
                    }
                    conn.Close();
                }

                Console.WriteLine("Everything looks good...");
            }
            catch (SqlException se)
            {
                Console.WriteLine("SQL Error: " + se.Message);
                throw se;
            }
            catch (Exception e)
            {
                Console.WriteLine("Normal Error: " + e.Message);
                throw e;
            }

            Console.ReadLine();
        }
    }
}

The stored procedure raises an error of level 16 which as far as I've read should be enough to be error-worthy. However control never jumps to the catch block; it just chugs through like nothing went wrong.

I read someone suggest using OUTPUT parameters... which I can do, but it seems like I must be missing something fundamental and simple here. Can anyone help?

UPDATE: It would appear if I use ExecuteNonQuery() the errors propagate just fine. However my use case is a procedure which performs DML and returns data based on that DML. Maybe the answer is "don't do that" but it'd be nice to know if there's a way to simply catch an error when grabbing results.


Solution

  • The reason is because the raise error is after the end of the first result set in the data reader and we’ll only get the error if we call NextResult() on the data reader!

    When using a SqlDataReader it will only iterate over the first result set, which in this case will be the select in the stored procedure

    Try and see more details here

    using (SqlDataReader rdr = cmd.ExecuteReader())
    {
        while (!rdr.IsClosed())
        {   
            while (rdr.Read())
            {
                Console.WriteLine(rdr.GetValue(0).ToString());
            }
    
            if (!rdr.NextResult())
            {
                rdr.Close();
            }
        }
    }