TransactionScopeにより、2番目のサービスエラーが発生しても部分的な更新が可能
-
05-07-2019 - |
質問
関連する質問と実装された提案をうまく調べましたが、.NET TransactionScopeにはまだ問題があります。
メソッドから2つのWCFサービスを呼び出していますが、2番目のサービス(この場合は意図的に)でエラーが発生しても、最初のサービスはロールバックしません。問題を実証する簡単なテストアプリケーションを作成しました。
呼び出しメソッドは次のとおりです。
private void TryATransaction()
{
ServiceReference1.IService1 service = new ServiceReference1.Service1Client();
ServiceReference2.IService1 service2 = new ServiceReference2.Service1Client();
using (TransactionScope scope = new TransactionScope())
{
service.TestMethod("one");
service2.UpdateSomethingElse("two");
scope.Complete();
}
}
最初のサービスは次のようになります(接続は匿名になります):
public bool TestMethod(string anything)
{
using (TransactionScope scope = new TransactionScope())
{
// connect to a db
using (SqlConnection connection = new SqlConnection("Data Source=DATASOURCE;Initial Catalog=DATABASE;Integrated Security=SSPI;Transaction Binding=Explicit Unbind;"))
{
connection.Open();
// save something
SqlCommand command1 = new SqlCommand("EXECUTE [DATABASE].[dbo].[uspUpdateCustomer] 3, 'test@test.com', 1, null", connection);
command1.ExecuteNonQuery();
connection.Close();
}
scope.Complete();
}
return true;
}
2番目のサービスは、データベースの別のフィールドを更新することを除いて、最初のサービスと同じです。
1)エラーを強制せずにこれを実行すると、すべてのフィールドが正常に更新されます。
2)これを実行して2番目のサービスでエラーを強制すると、最初のサービスからの更新がデータベースにコミットされますが、2番目のサービスはロールバックされます(エラーはExecuteNonQueryステートメントの後です)。
このサンプルコードは、基本的にここにあるサンプルに従います。 http://msdn.microsoft.com/en-us/ library / system.transactions.transactionscope.aspx
そして、明示的なバインド解除を追加しました。これは、他の関連する質問で推奨されました。
ご提案は大歓迎です-必要に応じて詳細を追加できます。
追加情報
配布された識別子は常に00000000-0000-0000-0000-000000000000のようです-これが手掛かりかどうかわかりません。
これは、分散IDとローカルIDのdubug出力です(この順序で)
現在のトランザクションは 00000000-0000-0000-0000-000000000000- f6446876-496d-488c-a21c-1e4c4295d50c:8
現在のトランザクションは 00000000-0000-0000-0000-000000000000- 7edd5ba3-7f5a-42af-b9ca-37b3862c26a7:2
現在のトランザクションは 00000000-0000-0000-0000-000000000000- 6fa0e3f7-b655-40ad-8bdd-f0670de79a49:2
トランザクションは、サンプルアプリのaspxページの背後にあるコードを介して開始されています。
解決
編集:後方に物事がありました。
ほとんどの場合、問題はWCFとともにトランザクションを使用していることが原因です。
トランザクションサービスに関するこちらの記事をご覧ください。特に、クライアント/サービスモードトランザクションに関する部分。
基本的に必要なこと:
- OperationContractインターフェイスメソッドで
[TransactionFlow(TransactionFlowOption.Mandatory)]
属性を設定します - 実装に
[OperationBehavior(TransactionScopeRequired = true)]
属性を設定します -
TransactionFlowBindingElement
をサービスのBindingContextに追加します - クライアントのTransactionScopeを使用しているため、サービスの実装から
TransactionScope
を削除します。
正常に完了した各サービスメソッドは、トランザクションが成功するよう投票します(デフォルトのTransactionAutoComplete = true OperationBehaviorを使用している場合)。
例外のためにサービスメソッドが失敗した場合、トランザクションが失敗したと投票します。