Well, I'm a client code monkey that works a lot with databases. This is how I deal with this.
Exceptions (raiseerrors) that occur in SQL propagate back to the caller. This will include ref constraints, unique index violations, more serious issues, etc. In principle, everything that does not lead to the normal operation of the data should be distributed back.
The C # caller must have the following:
catch (SQLException sqlEx)
And then handle the exception as needed. They must have a special SQLException handler. It is important.
I generally can not move away from the output parameters, because I believe that they are associated with the transmitted data, and not with error messages, in addition, I can check the exception for the SQL Server error code, so all the data that we need should be that is the exception.
In addition, in some cases with SQL Server, we have stored procedures that can cause a "business-type exception". In these cases, we add an error number (above 50,000) and raise this error in the stored procedure when necessary. In general, we try to minimize them, because it adds complexity, but in some cases we find that they are necessary.
Now, since the client is catching a SQLException, they can look at the error code returned by SQL Server in the exception, and then take some special action (if necessary) when the exception is caught and the error number is a specific value. This allows you to use a secondary level of error handling based on the error code, if necessary for user errors (> 50,000).
It also allows database administrators to raise user errors and have a consistent way of interacting with client code. Database administrators then had to tell the monkey client code what user errors were such that they could prepare for them.
I usually don’t use return codes to handle errors, although I can see how they can be used, but this means that in the code monkey layer you need more logic to look at and process the return code. If this is a problem, I want an exception, because then I can deal with them sequentially. If I need to also look at return codes, now there are several ways to handle errors.