我有一个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.NETMVCWeb应用程序。它的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. Transaction.Transaction.当前
,它被设置为有效的System.Transaction.Transaction并且它具有一个分布式标识符集。
但是,当我在WCF服务中时,System. Transaction.Transaction.Curren设置为null。
所以我的事务似乎没有被传递到服务。我希望事务是可选的,但是当有事务时,显然,我希望它被使用。
我错过了一步吗?
更新
根据BNL的评论,我现在已经使TransactionScope必需=true(更新了上面的代码以反映这一点)。
如果我在客户端中调用没有事务范围的方法,它们似乎运行良好(我假设服务创建了一个事务)。
如果我在客户端上创建一个事务范围并尝试调用该方法,第一次尝试调用它时,它会挂起大约55秒,然后我得到一个事务中止异常。我将事务的超时设置为3分钟(因为默认值为1分钟),但这继续发生在大约55秒。延迟似乎在客户端,因为调用自动生成的客户端代理方法后55秒,WCF服务才真正被调用。后续调用没有55秒延迟。代码如下:
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, new TimeSpan(0, 3, 0)))
{
_userMenuPermissionManager.SetPermissions(clientCode, menuPermissions);
ts.Complete();
}
异常发生在处置中,而不是在ts.完成()调用中。
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的回答之外,显然使用TransactionAuto完成=false
是不好的。除此之外,我不能说为什么会有问题,但是将其设置为true解决了我的问题,并且仍然允许客户端正确提交或回滚事务(我的印象是TransactionAuto完成=true
不是这种情况
在您的第二个代码块中,您有TransactionScope必需=false
。
如果您查看本文档的备注部分,您可以确定您没有事务。
http://msdn.microsoft.com/en-us/library/system.servicemodel.operationbehaviorattribute.transactionscoperequired.aspx
事务范围必填=假
绑定允许交易流=true
调用方流转事务=true
Result=方法在没有事务的情况下执行。
奇怪的是,没有指定的是,如果前两列都为真,但客户端没有流转事务会发生什么。看起来服务会创建一个新事务,但我不是100%确定。