wcf - 如何防止事务范围抛出我已经处理过的异常?

标签 wcf transactions

从概念上讲,我有一个 WCF 操作:

    [OperationBehavior(TransactionScopeRequired = true)]
    public void Foo() 
    {
        try { DAL.Foo(); return Receipt.CreateSuccessReceipt(); }
        catch (Exception ex) { return Receipt.CreateErrorReceipt(ex); }
    }

如果在执行 DAL 代码时出现问题(例如,外键约束违反),控制权会按我的预期传递给 catch block 。但是当方法返回时,似乎事务作用域已经嗅出事务失败,它决定最好抛出异常以确保通知调用者。

反过来,我的客户端应用程序没有得到我想要返回的收据,而是一个异常:
System.ServiceModel.FaultException:  
  The transaction under which this method call was executing was asynchronously aborted.

我的设计有什么问题?

我可以让服务不捕获任何东西,但这有它自己的问题,因为服务需要使用异常屏蔽并且客户端(系统内部的批处理工具)需要记录错误信息。该服务也会记录错误,但方式不同,并且与批处理不同。

最佳答案

这里要小心!如果您设置 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); }
    }
}

您可以通过创建一个将其全部包装在事务中的辅助方法来消除样板代码,或者您可以使用策略注入(inject)/拦截/方面来管理事务。
[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);
        }
    }
}

您提到您需要使用异常屏蔽。如果您不反对在发生错误时抛出错误,那么您可以使用企业库异常处理 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。

关于wcf - 如何防止事务范围抛出我已经处理过的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11900387/

相关文章:

WCF 服务客户端 : How do I debug the response parsing

c# - 使用WCF rest文件上传下载

c# - 实时、迭代应用架构

wcf - 当我们创建启用 SSL 的服务时,我们是否必须向每个客户端分发证书?

sql-server - 当两个事务发生冲突时会发生什么?

spring - @Transactional 和 JPA 对象参数

c# - 控制 WCF XML 输出中的命名空间前缀

Redis事务和watch命令

sql - 在主键冲突错误后继续事务

mysql - 一条SQL语句的事务