Müssen wir Transaktionen sowohl im C # -Code als auch in gespeicherten Prozeduren abwickeln?

14

Benötigen wir wirklich eine Transaktionsabwicklung sowohl im c # als auch im Datenbankspeicherprozess auf beiden Seiten?

C #:

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

Gespeicherte SQL-Prozedur:

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

Antworten:

20

Erstens sollten Sie in all Ihren Prozeduren immer eine ordnungsgemäße Transaktionsverarbeitung haben, damit es keine Rolle spielt, ob sie vom Anwendungscode, von einer anderen Prozedur, einzeln in einer Ad-hoc-Abfrage, von einem SQL Agent-Job oder auf andere Weise aufgerufen werden . Einzelne DML-Anweisungen oder Code, der keine Änderungen vornimmt, benötigen jedoch keine explizite Transaktion. Also, was ich empfehle, ist:

  • Verfügen Sie immer über die TRY / CATCH-Struktur, damit Fehler ordnungsgemäß in die Luft gesprudelt werden können
  • Wenn Sie mehrere DML-Anweisungen haben, können Sie optional die 3 Transaktionsbehandlungselemente in den folgenden Code aufnehmen (da eine einzelne Anweisung eine Transaktion für sich ist). Abgesehen vom Hinzufügen von zusätzlichem Code, der nicht speziell benötigt wird, schadet es jedoch nicht, die drei transaktionsbezogenen IF-Blöcke beizubehalten, wenn Sie eine konsistente Vorlage bevorzugen. Aber in diesem Fall würde ich noch empfehlen , nicht halten die 3 - Transaktion-IF - Blöcke für SELECT-only (dh read-only) Procs.

Wenn Sie zwei oder mehr DML-Anweisungen ausführen, müssen Sie eine der folgenden Anweisungen verwenden (dies kann auch für einzelne DML-Operationen durchgeführt werden, wenn eine konsistente Ausführung bevorzugt wird):

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;

Wenn Sie nur 1 DML-Anweisung oder nur ein SELECT ausführen, können Sie mit den folgenden Schritten davonkommen:

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;

Zweitens sollten Sie die Transaktion auf der App - Schicht behandeln nur , wenn Sie mehr als 1 Abfrage / gespeicherte Prozedur ausführen müssen , und sie alle brauchen in einer atomaren Operation gruppiert werden. Eine Single zu machen SqlCommand.Execute___muss nur in einem Versuch / einem Fang sein, aber nicht in einer Transaktion.

Aber ruft es an weh , eine Transaktion auf App-Ebene durchzuführen, wenn nur ein einziger Anruf getätigt wird? Wenn MSDTC (Microsoft Distributed Transaction Coordinator) erforderlich ist, ist es auf dem System etwas schwerer, dies auf der App-Ebene zu tun, wenn dies nicht ausdrücklich benötigt wird. Ich persönlich bevorzuge es, Transaktionen auf App-Ebene zu vermeiden, sofern dies nicht unbedingt erforderlich ist, da dadurch das Potenzial für verwaiste Transaktionen verringert wird (wenn vor dem Festschreiben oder Rollback ein Fehler mit dem App-Code aufgetreten ist). Ich habe auch festgestellt, dass es manchmal das Debuggen bestimmter Situationen etwas erschwert. Aber das wird gesagt, ich sehe nichts technisch falsch auch die Transaktion bei der App - Schicht Handhabung , wenn ein einzelner machen procAnruf; Auch hier ist eine einzelne DML-Anweisung eine eigene Transaktion und erfordert keine explizite Transaktionsbehandlung auf beiden Ebenen.

Solomon Rutzky
quelle