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.