pensé acerca de este tema por un tiempo y se le ocurrió una solución muy sencilla que no he visto antes, así que quería compartir esto:
Como es imposible volver a generar la mismo error, uno tiene que arrojar un error que es muy fácil de asignar al error original, por ejemplo, agregando un número fijo como 100000 a cada error del sistema.
Después de los mensajes recién asignados se añaden a la base de datos, es posible lanzar cualquier error del sistema, con un desplazamiento fijo de 100000
Este es el código para la creación de los mensajes asignados (esto tiene que ser hecho sólo una . tiempo para toda la instancia de SQL Server evitar conflictos con otros mensajes definidos por el usuario mediante la adición de un desplazamiento como 100.000 en este caso apropiado):
DECLARE messageCursor CURSOR
READ_ONLY
FOR select
message_id + 100000 as message_id, language_id, severity, is_event_logged, [text]
from
sys.messages
where
language_id = 1033
and
message_id < 50000
and
severity > 0
DECLARE
@id int,
@severity int,
@lang int,
@msgText nvarchar(1000),
@withLog bit,
@withLogString nvarchar(100)
OPEN messageCursor
FETCH NEXT FROM messageCursor INTO @id, @lang, @severity, @withLog, @msgText
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
set @withLogString = case @withLog when 0 then 'false' else 'true' end
exec sp_addmessage @id, @severity, @msgText, 'us_english', @withLogString, 'replace'
END
FETCH NEXT FROM messageCursor INTO @id, @lang, @severity, @withLog, @msgText
END
CLOSE messageCursor
DEALLOCATE messageCursor
Y este es el código para elevar los códigos de error de nueva creación que tienen un arreglo desplazamiento del código original:
SELECT
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()
set @MappedNumber = @ErrorNumber + 100000;
RAISERROR
(
@MappedNumber,
@ErrorSeverity,
1
);
Hay una pequeña advertencia: en este caso, no puede proporcionar un mensaje por su cuenta.Pero esto se puede eludir agregando un% s adicional en la llamada sp_addmessage o cambiando todos los mensajes asignados a su propio patrón y suministrando los parámetros correctos en la llamada de raise raise. Lo mejor es configurar todos los mensajes con el mismo patrón, como '% s (línea:% d procedimiento:% s)% s', para que pueda suministrar el mensaje original como primer parámetro y anexar el procedimiento real y la línea y su propio mensaje como los otros parámetros.
En el cliente ahora puede hacer todo el manejo de excepciones ordinarias como si se hubieran lanzado los mensajes originales, solo tiene que acordarse de agregar el desplazamiento de la corrección. Incluso puede manejar excepciones originales y relanza con el mismo código como este:
switch(errorNumber)
{
case 8134:
case 108134:
{
}
}
por lo que ni siquiera tiene que saber si se trata de una relanza o el error original, siempre es correcto, incluso si se olvidó de maneja tus errores y el error original se deslizó.
Hay alguna mejora mencionada en otra parte sobre la generación de mensajes que no puede generar o que no puede usar. Esos quedan aquí para mostrar solo el núcleo de la idea.
No creo que necesite transacciones si solo quiere detectar el error original: simplemente no use TRY/CATCH o RAISERROR() en el lado de T-SQL. Además, el enfoque de TransactionScope funciona bien para consultas simples o SP, pero hay casos de esquina con SP más complejos en los que no hace lo correcto. – RickNZ
@RickNZ, olvidé mencionar en la pregunta que estoy usando dos instrucciones INSERT y que es por eso que se necesita la transacción. Buena contribución sin embargo! –