Siamo tenuti a gestire la transazione nel codice C # e nella procedura memorizzata


14

Abbiamo davvero bisogno della gestione delle transazioni in c # e del processo di archiviazione del database su entrambi i lati

C #:

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

SQL stored procedure:

Create process
As
Begin try
    Begin transaction
    Commit
End try
Begin catch
    Rollback
End catch

Risposte:


20

Innanzitutto , dovresti sempre avere una corretta gestione delle transazioni in tutte le tue procedure in modo che non abbia importanza se vengono chiamate dal codice dell'app, da un'altra procedura, individualmente in una query ad hoc, da un processo di SQL Agent o in altro modo . Ma singole istruzioni DML, o codice che non apporta alcuna modifica, non necessitano di una Transazione esplicita. Quindi, ciò che sto raccomandando è:

  • Avere sempre la struttura TRY / CATCH in modo che gli errori possano essere correttamente bollati
  • Facoltativamente, includere le 3 parti di gestione delle Transazioni nel codice seguente se si hanno più istruzioni DML (poiché una singola istruzione è una transazione in sé). TUTTAVIA, al di fuori dell'aggiunta di qualche codice aggiuntivo laddove non sia specificamente necessario, se si preferisce avere un modello coerente, non fa male mantenere i 3 blocchi IF relativi alle Transazioni. In tal caso, consiglierei comunque di non conservare i 3 blocchi IF relativi alla transazione per i processi di sola selezione (ovvero di sola lettura).

Quando fai 2 o più istruzioni DML, tu necessario utilizzare qualcosa in linea con quanto segue (che può essere fatto anche per singole operazioni DML se si preferisce essere coerenti):

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;

Quando si esegue solo 1 istruzione DML o solo un SELECT, è possibile cavarsela con le seguenti opzioni:

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;

Secondo , è necessario gestire la transazione a livello di app solo se è necessario eseguire più di 1 query / stored procedure e tutte devono essere raggruppate in un'operazione atomica. Fare un singolo SqlCommand.Execute___deve essere solo in un tentativo / cattura, ma non in una Transazione.

Ma fa male fare una Transazione a livello di app quando si effettua una sola chiamata? Se richiede MSDTC (Microsoft Distributed Transaction Coordinator), è un po 'più pesante sul sistema farlo a livello di app quando non è espressamente necessario. Personalmente, preferisco evitare le transazioni basate sul livello dell'app a meno che non sia assolutamente necessario in quanto riducono il potenziale delle transazioni orfane (se qualcosa è andato storto con il codice dell'app prima di eseguire il commit o il rollback). Ho anche scoperto che a volte rende il debug di alcune situazioni un po 'più difficile. Detto questo, non vedo nulla di tecnicamente sbagliato nel gestire anche la transazione a livello di app quando si effettua un singolo procchiamata; ancora una volta, una singola istruzione DML è una transazione propria e non richiede alcuna gestione esplicita delle transazioni su entrambi i livelli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.