Distinguish errors I raise from other SQL Server errors



  • In my stored procedures, when a business rule is broken I raise an error that bubbles up to the C# client app and gets displayed to the user. e.g.

    RAISERROR('Hey, you cannot do that because blah blah blah', 16, 1);
    

    I want to distinguish errors that I raise from other SQL Server errors, because I only want my errors to get displayed.

    I think these are the only ways to send a message to the client app that this is a user message to display: Severity level, State, Return Code. But I think I should leave the severity level.

    • How to I tell my client app?
    • And what code or number should I use that is not already being used?
    • Or is there another way I have not considered?

    Or should I do this:

    THROW 50000, 'Hey, you cannot do that!', 1;  
    

    EDIT (13-April-2022) I asked Erland Sommarskog whose response helped me realise that if you use this

    RAISERROR('Hey, you cannot do that because blah blah blah', 16, 1);
    

    ...then the client app will always get 50000 as the error number. And with RAISERROR you can use parameterized messages. You just need to be aware that RAISERROR will not exit the batch. So, for me, RAISERROR wins.



  • You should be using https://docs.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql?view=sql-server-ver15 anyway as https://docs.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver15 is somewhat deprecated per Microsoft's docs:

    The RAISERROR statement does not honor SET XACT_ABORT. New applications should use THROW instead of RAISERROR.

    Per the docs on THROW, the error_number parameter must be a number greater than or equal to 50000 and less than or equal to 2147483647. (The first 49,999 error codes are already reserved for https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver15 .)

    If you are using C# in your client app, then when your application code receives an exception from your SQL Server, that exception object will be castable to a https://docs.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlexception?view=sqlclient-dotnet-standard-4.1 object. ( https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlexception?view=dotnet-plat-ext-6.0 object if you're still using the old assembly reference.) This will expose all the properties of the custom exception you threw, and specifically the https://docs.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlexception.number?view=sqlclient-dotnet-standard-4.1#microsoft-data-sqlclient-sqlexception-number property will contain your custom error code.

    Personally, I like to keep a collection of Enums for each user defined error code I'm utilizing in my SQL Server instance.


    If you do choose to continue to use RAISERROR you can also look into creating and using your own user defined error messages with https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-addmessage-transact-sql?view=sql-server-ver15 as a way to refactor the actual message to a custom error code in SQL Server, so that you can reference the same object each time you throw the same exception. (I've personally never used this feature before though.) But I'd personally recommend sticking to THROW.




Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2