C#コードおよびストアドプロシージャでトランザクションを処理する必要がありますか


14

データベースストアプロセスとC#の両方でトランザクション処理が本当に必要ですか?

C#:

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

SQLストアドプロシージャ:

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

回答:


20

まず、すべてのプロシージャで常に適切なトランザクション処理を行う必要があります。これにより、アプリケーションコード、別のプロシージャ、個別のアドホッククエリ、SQLエージェントジョブ、またはその他の手段によって呼び出されるかどうかは関係ありません。 。ただし、単一のDMLステートメント、または変更を行わないコードは、明示的なトランザクションを必要としません。だから、私がお勧めしているのは:

  • エラーを適切にバブルアップできるように、常にTRY / CATCH構造を持っている
  • 複数のDMLステートメントがある場合は、オプションで3つのトランザクション処理部分を以下のコードに含めます(1つのステートメント自体がトランザクションであるため)。ただし、特に必要のないコードを追加する以外に、一貫したテンプレートを使用したい場合は、3つのトランザクション関連のIFブロックを保持するのに問題はありません。ただし、その場合は、SELECT専用(読み取り専用)プロシージャの3つのトランザクション関連IFブロックを保持しないことをお勧めします。

2つ以上のDMLステートメントを実行する場合、次の行に沿って何かを使用する必要があります(一貫性を保ちたい場合は、単一のDML操作でも実行できます)。

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;

DMLステートメントを1つだけ、またはSELECTを1つだけ実行すると、次のようになります。

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;

次に、複数のクエリ/ストアドプロシージャを実行する必要があり、それらをすべてアトミック操作にグループ化する必要がある場合にのみ、アプリ層でトランザクションを処理する必要があります。シングルをするSqlCommand.Execute___ことは、try / catchでのみ必要であり、Transactionでは必要ありません。

しかし、単一の呼び出しのみを行う場合、アプリ層でトランザクションを行うことは痛いですか?MSDTC(Microsoft Distributed Transaction Coordinator)が必要な場合、明示的に必要でない場合にアプリ層でこれを行うと、システム上で少し重くなります。個人的には、孤立したトランザクションの可能性を削減するため、絶対に必要な場合を除き、アプリレイヤーベースのトランザクションを避けることを好みます(コミットまたはロールバックを行う前にアプリのコードに何か問題が発生した場合)。また、特定の状況のデバッグが少し難しくなることもあります。しかし、それビーイングは、私は何も表示されない、と述べた技術的に間違っても、単一の際にアプリ層でトランザクションを処理します PROCをコール; 繰り返しますが、単一のDMLステートメントはそれ自体のトランザクションであり、どちらの層でも明示的なトランザクション処理を必要としません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.