Bueno, soy un mono de código de cliente que se ocupa mucho de las bases de datos. Así es como lo manejo.
Las excepciones (raiseerrors) que ocurren en SQL se propagan de vuelta a la persona que llama. Esto incluiría restricciones de referencia, violaciones de índices únicos, problemas más serios, etc. Básicamente, cualquier cosa que no haga que la operación de datos ocurra normalmente debe propagarse.
La persona que llama C# debería tener esto:
catch (SQLException sqlEx)
Y luego maneje la excepción según sea necesario. Deben tener un controlador de SQLException específico. Esto es importante.
Por lo general, me mantengo alejado de los parámetros de salida porque considero que están relacionados con los datos que se transportan y no con ningún mensaje de error. Además, puedo inspeccionar la excepción del código de error de SQL Server para que todos los datos que necesitamos estén en esa excepción.
Además, en algunos casos con SQL Server, tenemos procedimientos almacenados que podrían generar "excepciones de tipo comercial". En esos casos, agregamos un número de error personalizado (por encima de 50000) y generamos ese error en el procedimiento almacenado cuando sea necesario. En general, tratamos de mantenerlos al mínimo porque agrega complejidad, pero en algunos casos, hemos encontrado que son necesarios.
Ahora, dado que el cliente está detectando la SQLException, puede mirar el código de error devuelto por SQL Server en la excepción y luego tomar cualquier acción especial (si es necesario) cuando se detecta la excepción y el número de error es un valor determinado. Esto permite un nivel secundario de manejo de errores basado en el código de error si es necesario para los errores personalizados (>50000).
Esto también permite que los administradores de bases de datos generen errores personalizados y que el código del cliente tenga una forma coherente de tratarlos. Luego, los DBA tendrían que decirle al mono de código del cliente cuáles fueron los errores personalizados para que pudieran prepararse para ellos.
Por lo general, no uso los códigos de retorno para el manejo de errores, aunque puedo ver cómo podrían usarse, pero eso significa más lógica en la capa del mono del código para mirar y tratar el código de retorno. Si son un problema, quiero que me devuelvan una excepción, porque entonces puedo lidiar con ellos de manera consistente. Si también tengo que mirar los códigos de retorno, ahora hay varias vías para el manejo de errores.