トランザクションのコミットエラーの処理Handling transaction commit failures


Ef 6.1 以降のみ -このページで説明した機能、api などが Entity Framework 6.1 で導入されました。EF6.1 Onwards Only - The features, APIs, etc. discussed in this page were introduced in Entity Framework 6.1. 以前のバージョンを使用している場合、一部またはすべての情報は適用されません。If you are using an earlier version, some or all of the information does not apply.

6.1 の一部として、EF の新しい接続回復性機能が導入されています。これは、一時的な接続エラーがトランザクションのコミットの受信確認に影響する場合に、自動的に検出して回復する機能です。As part of 6.1 we are introducing a new connection resiliency feature for EF: the ability to detect and recover automatically when transient connection failures affect the acknowledgement of transaction commits. このシナリオの詳細については、ブログ SQL Database 記事「 接続とべき等の問題」を参照してください。The full details of the scenario are best described in the blog post SQL Database Connectivity and the Idempotency Issue. つまり、このシナリオでは、トランザクションのコミット中に例外が発生すると、次の2つの原因が考えられます。In summary, the scenario is that when an exception is raised during a transaction commit there are two possible causes:

  1. サーバーでトランザクションのコミットに失敗しましたThe transaction commit failed on the server
  2. サーバーでトランザクションのコミットに成功しましたが、接続の問題により成功通知がクライアントに到達しませんでしたThe transaction commit succeeded on the server but a connectivity issue prevented the success notification from reaching the client

最初の状況が発生した場合、アプリケーションまたはユーザーは操作を再試行できますが、2番目の状況が発生した場合は、再試行を避け、アプリケーションを自動的に回復できます。When the first situation happens the application or the user can retry the operation, but when the second situation occurs retries should be avoided and the application could recover automatically. 課題は、コミット中に例外が報告された実際の理由を検出する機能がないということです。アプリケーションは適切な措置を選択できません。The challenge is that without the ability to detect what was the actual reason an exception was reported during commit, the application cannot choose the right course of action. EF 6.1 の新機能では、トランザクションが成功した場合に EF がデータベースを再確認し、適切な操作を透過的に行うことができます。The new feature in EF 6.1 allows EF to double-check with the database if the transaction succeeded and take the right course of action transparently.

機能の使用Using the feature

この機能を有効にするには、 Dbconfigurationのコンストラクターにsettransactionhandlerを呼び出す必要があります。In order to enable the feature you need include a call to SetTransactionHandler in the constructor of your DbConfiguration. Dbconfigurationの詳細については、「コードベースの構成」を参照してください。If you are unfamiliar with DbConfiguration, see Code Based Configuration. この機能は、EF6 で導入された自動再試行と組み合わせて使用できます。これは、一時的な障害により、トランザクションが実際にサーバー上でコミットに失敗した状況に役立ちます。This feature can be used in combination with the automatic retries we introduced in EF6, which help in the situation in which the transaction actually failed to commit on the server due to a transient failure:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;

public class MyConfiguration : DbConfiguration  
  public MyConfiguration()  
    SetTransactionHandler(SqlProviderServices.ProviderInvariantName, () => new CommitFailureHandler());  
    SetExecutionStrategy(SqlProviderServices.ProviderInvariantName, () => new SqlAzureExecutionStrategy());  

トランザクションの追跡方法How transactions are tracked

この機能を有効にすると、EF によって __Transactionsというデータベースに新しいテーブルが自動的に追加されます。When the feature is enabled, EF will automatically add a new table to the database called __Transactions. EF によってトランザクションが作成されるたびに、このテーブルに新しい行が挿入されます。コミット中にトランザクションエラーが発生した場合は、その行が存在するかどうかがチェックされます。A new row is inserted in this table every time a transaction is created by EF and that row is checked for existence if a transaction failure occurs during commit.

EF は、不要になったテーブルから行を排除するのに最適な作業を行いますが、アプリケーションが途中で終了した場合にテーブルを拡張できます。そのため、場合によっては、テーブルを手動で消去する必要があります。Although EF will do a best effort to prune rows from the table when they aren’t needed anymore, the table can grow if the application exits prematurely and for that reason you may need to purge the table manually in some cases.

以前のバージョンのコミットエラーを処理する方法How to handle commit failures with previous Versions

EF 6.1 より前では、EF 製品のコミットエラーを処理するメカニズムがありませんでした。Before EF 6.1 there was not mechanism to handle commit failures in the EF product. 以前のバージョンの EF6 に適用できるこのような状況に対処するには、いくつかの方法があります。There are several ways to dealing with this situation that can be applied to previous versions of EF6:

  • オプション 1-何もしないOption 1 - Do nothing

    トランザクションのコミット中に接続エラーが発生する可能性は低いため、この状態が実際に発生した場合にアプリケーションが失敗する可能性があります。The likelihood of a connection failure during transaction commit is low so it may be acceptable for your application to just fail if this condition actually occurs.

  • オプション 2-データベースを使用して状態をリセットするOption 2 - Use the database to reset state

    1. 現在の DbContext を破棄しますDiscard the current DbContext
    2. 新しい DbContext を作成し、データベースからアプリケーションの状態を復元します。Create a new DbContext and restore the state of your application from the database
    3. 最後の操作が正常に完了していない可能性があることをユーザーに通知しますInform the user that the last operation might not have been completed successfully
  • オプション 3-トランザクションを手動で追跡するOption 3 - Manually track the transaction

    1. トランザクションの状態を追跡するために使用するデータベースに、追跡されていないテーブルを追加します。Add a non-tracked table to the database used to track the status of the transactions.
    2. 各トランザクションの開始時に、テーブルに行を挿入します。Insert a row into the table at the beginning of each transaction.
    3. コミット中に接続が失敗した場合は、データベース内の対応する行が存在するかどうかを確認します。If the connection fails during the commit, check for the presence of the corresponding row in the database.
      • 行が存在する場合は、トランザクションが正常にコミットされたため、通常どおり続行します。If the row is present, continue normally, as the transaction was committed successfully
      • 行が存在しない場合は、実行方法を使用して現在の操作を再試行します。If the row is absent, use an execution strategy to retry the current operation.
    4. コミットが成功した場合は、対応する行を削除して、テーブルの増加を回避します。If the commit is successful, delete the corresponding row to avoid the growth of the table.

このブログ投稿 には、SQL Azure でこれを実現するためのサンプルコードが含まれています。This blog post contains sample code for accomplishing this on SQL Azure.