这里要小心!如果您设置 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);
}
}
}
您提到您需要使用异常屏蔽。如果您不反对在发生错误时抛出错误,那么您可以使用企业库异常处理块的异常屏蔽,它还可以让您在退出时记录信息(如果您愿意)。
如果您决定走这条路,您的代码将如下所示:
[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。