Por norma general, me gusta codificar la lógica de acceso a datos en programa, en lugar de en procedimientos almacenados en base de datos. Esta aproximación no es muy óptima (como se suele decir, al César lo que es del César y a Dios lo que es de Dios), ya que el entorno transaccional estará mejor gestionado por el SGBD que por el código .NET.
Si queremos trasladar la responsabilidad de gestionar la transacción al SGBD, deberemos saber en primer lugar que se declara del siguiente modo:
BEGIN TRAN -- Ejecución de sentencias COMMIT TRAN
Como imaginaremos, la primera sentencia «arranca» la transacción, mientras que la última la compromete. Pero ¿y si ocurre un error en alguna de las sentencias? ¿Cómo lo detectamos?
En el siguiente ejemplo, utilizaremos variables enteras para trazar los fallos y realizar un ROLLBACK en el caso de que la transacción falle.
Se declarará una variable de tipo INT por cada sentencia a ejecutar, y se inician a 0. Se comprueba, después de cada sentencia ejecutada, la variable @@Error, y se almacena en las variables anteriormente creadas. Si es distinta de 0, hay un error, por lo que se añade información al mensaje de error.
Al final de la transacción, si todas las variables son 0, se hace el COMMIT. Si una de ellas es distinta de cero, significará que existe un error y se hace el Rollback, mostrando el mensaje de error.
DECLARE @CadenaError varchar(1000); DECLARE @CodigosError varchar(1000); DECLARE @ErrorSentencia1 int; DECLARE @ErrorSentencia2 int; DECLARE @ErrorSentencia3 int; SET @ErrorSentencia1 = 0; SET @ErrorSentencia2 = 0; SET @ErrorSentencia3 = 0; SET @CadenaError = 'Error al realizar actualización: '; SET @CodigosError = 'Códigos de error: '; -- Inicio de la transacción BEGIN TRAN -- Primera actualización UPDATE Usuario SET Nombre = 'Elena' WHERE Nombre = "Helena"; -- Almacenamos el posible código de error SET @ErrorSentencia1 = @@Error; IF (@ErrorSentencia1 0) BEGIN SET @CadenaError = @CadenaError + 'Actualización 1; ' SET @CodigosError = @CodigosError + CAST(@ErrorSentencia1 AS VARCHAR(10)) + '; '; END -- Segunda actualización UPDATE Perfil SET Nombre = 'Administrador' WHERE Nombre = "Admin"; -- Almacenamos el posible código de error SET @ErrorSentencia2 = @@Error; IF (@ErrorSentencia2 0) BEGIN SET @CadenaError = @CadenaError + 'Actualización 2; ' SET @CodigosError = @CodigosError + CAST(@ErrorSentencia2 AS VARCHAR(10)) + '; '; END -- Tercera actualización UPDATE UsuarioPerfil SET CodigoUsuario = 1 WHERE CodigoPerfil < 3; -- Almacenamos el posible código de error SET @ErrorSentencia3 = @@Error; IF (@ErrorSentencia3 0) BEGIN SET @CadenaError = @CadenaError + 'Actualización 3; ' SET @CodigosError = @CodigosError + CAST(@ErrorSentencia3 AS VARCHAR(10)) + '; '; END -- En ausencia de errores, comprometemos la transacción IF (@ErrorSentencia1 = 0) AND (@ErrorSentencia2 = 0) AND (@ErrorSentencia3 = 0) BEGIN COMMIT TRAN; PRINT('Transacción realizada con éxito'); END ELSE BEGIN ROLLBACK TRAN; PRINT('Transacción abortada'); PRINT(@CadenaError); PRINT(@CodigosError); END
Con todo esto, obtendremos suficiente información (esperemos) como para detectar donde está el fallo.