Solución:
Siempre necesitas comprobar XACT_STATE()
, irrelevante de la XACT_ABORT
configuración. Tengo un ejemplo de una plantilla para procedimientos almacenados que necesitan manejar transacciones en el contexto TRY / CATCH en el manejo de excepciones y transacciones anidadas:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(),
@message = ERROR_MESSAGE(),
@xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
Hay algunos malentendidos en la discusión anterior.
Primero, siempre puede ROLLBACK una transacción … sin importar el estado de la transacción. Por lo tanto, solo tiene que verificar XACT_STATE antes de COMMIT, no antes de una reversión.
En cuanto al error en el código, querrá poner la transacción dentro del TRY. Luego en tu CATCH, lo primero que debes hacer es lo siguiente:
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION @transaction
Luego, después de la declaración anterior, puede enviar un correo electrónico o lo que sea necesario. (Para su información: si envía el correo electrónico ANTES de la reversión, definitivamente obtendrá el error “no se puede … escribir en el archivo de registro”).
Este problema fue del año pasado, así que espero que ya lo hayas resuelto 🙂 Remus te indicó la dirección correcta.
Como regla general … el TRY saltará inmediatamente al CATCH cuando haya un error. Luego, cuando esté en CATCH, puede usar XACT_STATE para decidir si puede comprometerse. Pero si siempre quiere ROLLBACK en la captura, entonces no necesita verificar el estado en absoluto.