简体   繁体   中英

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.

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

I can make three suggestions:

  1. Use CommandType.Text instead of CommandType.StoredProcedure . This may be fixed now, but a number of years ago I found that CommandType.StoredProcedure would always buffer message outputs into batches of about 50, while CommandType.Text would allow the messages to come back to C# right away.
  2. Add a WITH NOWAIT hint to your RAISERROR code.
  3. Don't forget about the FireInfoMessageEventOnUserErrors property. You do want to handle the InfoMessage event.

I'm not sure any of these will solve your problem, but they should give you some direction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM