トランザクション化しようとしている WCF サービスがあります。トランザクションを使用してサービスを呼び出すと、トランザクションは使用されず、トランザクションをロールバックすると、データベースの更新が行われます。
これはサービス インターフェイスです (テストした単一のメソッドのみを含めています。
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IUserMenuPermissionService
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[TransactionFlow(TransactionFlowOption.Allowed)]
void InsertUserMenuPermission(string userId, string menuId, int permission);
}
サービスの実装は次のとおりです。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class UserMenuPermissionService : IUserMenuPermissionService
{
private static IUserMenuPermissionProvider _provider = new CoreDataFactory().GetUserMenuPermissionProvider();
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void InsertUserMenuPermission(string userId, string menuId, int permission)
{
_provider.InsertUserMenuPermission(userId, menuId, permission);
}
}
実際の基盤となるプロバイダーは、トランザクションを直接処理するわけではありませんが、この時点では、それは私の問題ではなく、すぐに明らかになります。
WCF の app.config には次のものがあります。
<bindings>
<wsHttpBinding>
<binding name="TransactionBinding" transactionFlow="True" >
</binding>
</wsHttpBinding>
</bindings>
...
...
<service name="GEMS.Core.WCFService.UserMenuPermissionService">
<endpoint address="" bindingConfiguration="TransactionBinding" binding="wsHttpBinding" contract="SvcProvider.Core.WCFService.IUserMenuPermissionService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/SvcProvider.WebService/SvcProvider.Core.WCFService/UserMenuPermissionService/" />
</baseAddresses>
</host>
</service>
クライアントは ASP.NET MVC Web アプリです。Web構成には次のものがあります:
<wsHttpBinding>
<binding name="TransactionBinding" transactionFlow="true" />
</wsHttpBinding>
....
....
<endpoint address="http://localhost/GEMS.WebService/SvcProvider.Core.WCFService.UserMenuPermissionService.svc"
binding="wsHttpBinding" bindingConfiguration="TransactionBinding"
contract="UserMenuPermissionService.IUserMenuPermissionService"
name="WsHttpBinding_IUserMenuPermissionService" />
クライアントから Web サービスを呼び出すときは、トランザクション スコープ内で行います。を見るとSystem.Transactions.Transaction.Current
、有効な System.Transactions.Transaction に設定されており、DistributedIdentifier が設定されています。
ただし、WCF サービスを使用しているときは、System.Transactions.Transaction.Current が null に設定されます。
そのため、私のトランザクションがサービスに渡されていないようです。トランザクションをオプションにしたいのですが、トランザクションがある場合は、明らかにそれを使用したいと考えています。
私は一歩を踏み外しましたか?
アップデート
BNL からのコメントに従って、TransactionScopeRequired = true を作成しました (それを反映するために上記のコードを更新しました)。
クライアントでトランザクション スコープなしでメソッドを呼び出すと、それらは問題なく動作するように見えます (サービスがトランザクションを作成すると想定しています)。
クライアントでトランザクション スコープを作成し、メソッドを呼び出そうとすると、最初に呼び出そうとすると、約 55 秒間ハングし、その後トランザクション アボート例外が発生します。トランザクションのタイムアウトを 3 分に設定しました (デフォルトは 1 分であるため) が、約 55 秒で発生し続けました。WCF サービスが実際に呼び出される前に、自動生成されたクライアント プロキシ メソッドを呼び出してから 55 秒であるため、遅延はクライアント側にあるようです。後続の呼び出しには 55 秒の遅延はありません。コードは次のとおりです。
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, new TimeSpan(0, 3, 0)))
{
_userMenuPermissionManager.SetPermissions(clientCode, menuPermissions);
ts.Complete();
}
例外は、ts.Complete() 呼び出しではなく、処分で発生します。
at System.Transactions.TransactionStatePromotedAborted.PromotedTransactionOutcome(InternalTransaction tx)
at System.Transactions.TransactionStatePromotedEnded.EndCommit(InternalTransaction tx)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
at WebApp.Controllers.AdminController.SetUserMenuPermissions(String clientCode, MenuPermission[] permissions) in Controllers\\AdminController.cs:line 160
サービスが呼び出され、トランザクションが渡されます。サービストレースログによると、エラーはありません。すべてが順調に進んでいるように見えますが、最後にクライアントから中止を受け取り、トランザクションをロールバックすると思います。
クライアント サービス ログにも、55 秒の遅延を説明するような異常はありません。
ですから、もう少し進んでいますが (BNL に感謝します)、まだ完全には到達していません。
更新 2
追加した:
[ServiceBehavior(TransactionTimeout = "00:03:00", InstanceContextMode = InstanceContextMode.PerSession)]
サービスの実装に55秒の遅延が2分55秒の遅延に変わったため、トランザクションがタイムアウトしているように見え、サービスメソッドが(トランザクションで)呼び出され、サービスがすべての処理を実行してからクライアントは最後にアボートを送信します。個別のTransactionScopes
アボートでの後続の呼び出しはすぐに...
アップデート 3
明示的なトランザクション内で発生していない、見逃した以前の呼び出しが原因でタイムアウトが発生したようです。トランザクションを自動的にコミットしないため、コミットされていないトランザクションがあったためにハングしていました。これで、すべての呼び出しがトランザクションにラップされました。最初の呼び出しはすぐに中止されます。ただし、サービスまたはクライアントのトレース ログに問題の兆候はまだありません。内部例外はありません。ただの中絶...
更新 4
BNLの回答に加えて、どうやら使用TransactionAutoComplete = false
は悪いです。それを超えて、なぜそれが問題だったのかは本当にわかりませんが、trueに設定すると問題が解決し、クライアントはトランザクションを適切にコミットまたはロールバックできます(これはそうではなかったという印象を受けましたTransactionAutoComplete = true