Bem, eu sou um macaco de código de cliente que lida muito com bancos de dados. Aqui está como eu lido com isso.
Exceções (raiseerrors) que acontecem no SQL são propagadas de volta para o chamador. Isso inclui restrições de referência, violações de índice exclusivas, problemas mais sérios, etc. Basicamente, qualquer coisa que não faça a operação de dados ocorrer normalmente deve ser propagada de volta.
O chamador C# deve ter isso:
catch (SQLException sqlEx)
E, em seguida, trate a exceção conforme necessário. Eles devem ter um manipulador SQLException específico. Isso é importante.
Geralmente, fico longe dos parâmetros de saída porque considero que eles estão relacionados aos dados que estão sendo transportados e não a mensagens de erro, além disso, posso inspecionar a exceção do código de erro do SQL Server para que todos os dados de que precisamos estejam nessa exceção.
Além disso, em alguns casos com o SQL Server, temos Stored Procedures que podem gerar "exceções do tipo comercial". Nesses casos, adicionamos um número de erro personalizado (acima de 50.000) e geramos esse erro no procedimento armazenado quando necessário. Em geral, tentamos mantê-los no mínimo porque adiciona complexidade, mas em alguns casos, achamos que eles são necessários.
Agora, como o cliente está capturando a SQLException, ele pode examinar o código de erro retornado pelo SQL Server na exceção e, em seguida, executar qualquer ação especial (se necessário) quando a exceção for capturada e o número do erro for um determinado valor. Isso permite um nível secundário de tratamento de erros com base no código de erro, se necessário para os erros personalizados (>50.000).
Isso também permite que os DBAs gerem erros personalizados e façam com que o código do cliente tenha uma maneira consistente de lidar com eles. Os DBAs teriam então que dizer ao macaco de código do cliente quais eram os erros personalizados para que pudessem se preparar para eles.
Normalmente, não uso os códigos de retorno para fins de tratamento de erros, embora possa ver como eles podem ser usados, mas isso significa mais lógica na camada de macaco de código para examinar e lidar com o código de retorno. Se eles são um problema, quero uma exceção de volta, porque assim posso lidar com eles de forma consistente. Se eu também tiver que olhar para os códigos de retorno, agora existem várias vias de tratamento de erros.