我有一个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应用程序。它的网络配置有:
<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可修复我的问题,并且仍然允许客户端正确提交或回滚交易(我的印象是{{1}不是这种情况}
答案 0 :(得分:2)
在第二个代码块中,您有TransactionScopeRequired = false
。
如果您查看本文档的“备注”部分,您可以确定您没有交易。
TransactionScopeRequired = false
Binding允许transaction flow = true
来电流动交易=真
Result =方法在没有事务的情况下执行。
奇怪的是,未指定的是如果前两列都为真但客户端不流动事务会发生什么。看起来该服务将创建一个新的交易,但我不是百分之百确定。