我在概念上有这样的WCF操作:
[OperationBehavior(TransactionScopeRequired = true)]
public void Foo()
{
try { DAL.Foo(); return Receipt.CreateSuccessReceipt(); }
catch (Exception ex) { return Receipt.CreateErrorReceipt(ex); }
}
如果在执行DAL代码时出现问题(例如,外键约束违规),控制会按照我的预期传递给catch块。但是当方法返回时,似乎事务范围已经嗅出事务失败,并且它决定最好抛出异常以确保通知调用者它。
反过来,我的客户端应用程序没有收到我想要返回的收据,而是一个例外:
System.ServiceModel.FaultException:
The transaction under which this method call was executing was asynchronously aborted.
我的设计出了什么问题?
我可以让服务没有捕获任何东西,但这有它自己的问题,因为服务需要使用异常屏蔽,而客户端(系统内部的批处理工具)需要记录错误信息。该服务也会记录错误,但不会以与批处理相同的方式和相同的位置记录。
答案 0 :(得分:7)
这里要小心!如果设置TransactionAutoComplete = true,那么如果服务正常返回,则将提交事务。只有当存在未处理的异常时(由于您捕获异常并返回收据消息,大多数情况下您没有这种异常)才会回滚事务。见http://msdn.microsoft.com/en-us/library/system.servicemodel.operationbehaviorattribute.transactionautocomplete.aspx。
考虑一下您成功执行了一些DAL调用的情况,但发生了一些其他异常(例如NullReferenceException)。现在,当方法完成时将提交事务,因为没有发生未处理的异常,但客户端收到ErrorReceipt。
对于您的方案,我认为您必须自己管理交易。例如:
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public Receipt Foo()
{
// Create TransactionScope using the ambient transaction
using (var scope = new TransactionScope() )
{
try { DAL.Foo(); return Receipt.CreateSuccessReceipt(); scope.Complete(); }
catch (Exception ex) { return Receipt.CreateErrorReceipt(ex); }
}
}
您可以通过创建一个帮助方法来消除样板代码,该方法将所有内容包装在事务中,或者您可以使用策略注入/拦截/方面来管理事务。
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public Receipt Foo()
{
return ProcessWithTransaction(() =>
{
DAL.Foo();
return Receipt.CreateSuccessReceipt();
}
, (ex) =>
{
return Receipt.CreateErrorReceipt(ex);
}
);
}
T ProcessWithTransaction<T>(Func<T> processor, Func<Exception, T> exceptionHandler)
{
using (var scope = new TransactionScope())
{
try
{
T returnValue = processor();
scope.Complete();
return returnValue;
}
catch (Exception e)
{
return exceptionHandler(e);
}
}
}
您提到需要使用异常屏蔽。如果您在发生错误时不反对抛出错误,那么您可以使用Enterprise Library Exception Handling Block的异常屏蔽,它还允许您在出路时记录信息(如果您愿意)。
如果您决定采用这种方式,您的代码将如下所示:
[OperationBehavior(TransactionScopeRequired = true)]
public void Foo()
{
// Resolve the default ExceptionManager object from the container.
ExceptionManager exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
exManager.Process(() =>
{
DAL.Foo();
return Receipt.CreateSuccessReceipt();
},
"ExceptionShielding");
}
企业库(通过配置)将捕获任何异常,并将其替换为返回给客户端的新FaultException。
答案 1 :(得分:0)
[OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)]
大概是因为事件现在在错误发生后立即回滚,而不是在范围超出范围时异步:D,这表现得像我预期的事情原来的行为,我可以保留我的设计,因为它是
(在我尝试这个时,我已经写了这个问题。希望发布它Q&amp; A样式比没有发布问题更有帮助。)