¿Estamos obligados a manejar la transacción en el código C #, así como en el procedimiento almacenado?

14

¿Realmente requerimos el manejo de transacciones en C # y el proceso de almacenamiento de la base de datos en ambos lados?

C#:

Using(transaction with transaction scope)
{
     Execute stored proc;
     Transaction. Complete;
}

Procedimiento almacenado de SQL:

Create process
As
Begin try
    Begin transaction
    Commit
End try
Begin catch
    Rollback
End catch
Rakesh Gaur
fuente

Respuestas:

20

Primero , siempre debe tener un manejo adecuado de las transacciones en todos sus procedimientos para que no importe si son llamados por el código de la aplicación, por otro procedimiento, individualmente en una consulta ad-hoc, por un trabajo de Agente SQL u otro medio . Pero las declaraciones DML individuales, o el código que no realiza ninguna modificación, no necesita una transacción explícita. Entonces, lo que recomiendo es:

  • Siempre tenga la estructura TRY / CATCH para que los errores puedan aparecer correctamente
  • Opcionalmente, incluya las 3 piezas de manejo de transacciones en el código a continuación si tiene varias declaraciones DML (ya que una sola declaración es una transacción en sí misma). SIN EMBARGO, además de agregar algún código adicional donde no se necesita específicamente, si uno prefiere tener una plantilla consistente, entonces no está de más mantener los 3 bloques IF relacionados con la transacción. Pero en ese caso, aún recomendaría no mantener los 3 bloques IF relacionados con la transacción para procesos de solo SELECCIÓN (es decir, solo lectura).

Al hacer 2 o más instrucciones DML, debe usar algo similar a lo siguiente (que también se puede hacer para operaciones DML individuales si se prefiere ser coherente):

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT;

BEGIN TRY

    IF (@@TRANCOUNT = 0)
    BEGIN
        SET @InNestedTransaction = 0;
        BEGIN TRAN; -- only start a transaction if not already in one
    END;
    ELSE
    BEGIN
        SET @InNestedTransaction = 1;
    END;

    -- { 2 or more DML statements (i.e. INSERT / UPDATE / DELETE) }

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        COMMIT;
    END;

END TRY
BEGIN CATCH

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        ROLLBACK;
    END;

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

Al hacer solo 1 instrucción DML o solo un SELECT, puede salirse con la suya:

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;

BEGIN TRY

    -- { 0 or 1 DML statements (i.e. INSERT / UPDATE / DELETE) }

END TRY
BEGIN CATCH

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

En segundo lugar , debe manejar la transacción en la capa de aplicación solo si necesita ejecutar más de 1 consulta / procedimiento almacenado y todos deben agruparse en una operación atómica. Hacer un solo SqlCommand.Execute___solo tiene que estar en un intento / captura, pero no en una transacción.

Pero, ¿ duele hacer una transacción en la capa de la aplicación cuando solo hace una sola llamada? Si requiere MSDTC (Coordinador de transacciones distribuidas de Microsoft), entonces es un poco más pesado en el sistema hacer esto en la capa de la aplicación cuando no se necesita expresamente. Personalmente, prefiero evitar las transacciones basadas en la capa de la aplicación, a menos que sea absolutamente necesario, ya que reduce el potencial de transacciones huérfanas (si algo salió mal con el código de la aplicación antes de realizar el compromiso o la reversión). También he descubierto que a veces dificulta un poco la depuración de ciertas situaciones. Pero dicho esto, no veo nada técnicamente malo también maneja la transacción en la capa de aplicación al hacer un solo procllamada; de nuevo, una sola declaración DML es su propia transacción y no necesita ningún manejo de transacción explícito en ninguna de las capas.

Solomon Rutzky
fuente