web-dev-qa-db-ja.com

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

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
14
Rakesh Gaur

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

  • エラーを適切にバブルアップできるように、常にTRY/CATCH構造を持っている
  • 複数のDMLステートメントがある場合は、オプションで3つのトランザクション処理の部分を以下のコードに含めます(単一のステートメント自体がトランザクションであるため)。ただし、特に必要のない場所にコードを追加する以外に、一貫したテンプレートが必要な場合は、3つのトランザクション関連のIFブロックを保持しても問題はありません。しかし、その場合でも、SELECT専用(つまり、読み取り専用)のprocに対して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だけを実行する場合は、次の方法で回避できます。

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;

2番目、1つ以上のクエリ/ストアドプロシージャを実行する必要がある場合は、アプリレイヤーでトランザクションを処理する必要がありますonlyそれらはすべて、アトミック操作にグループ化する必要があります。単一のSqlCommand.Execute___は、try/catchにのみ必要ですが、トランザクションには必要ありません。

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

21
Solomon Rutzky