TransactionAbortedException隐藏WCF错误?

时间:2013-06-20 15:13:42

标签: c# wcf transactionscope

我有以下情况:

WCF客户端使用TransactionScope启动并将事务传播到WCF服务。

客户合同如下:

public interface IMyService
{
    [OperationContract]
    [FaultContract(typeof(MyException))]
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    bool DoSomeTransactionalWork();

    [OperationContract]
    [FaultContract(typeof(MyException))]
    bool DoSomeWork();

}

使用的代理不会生成,它基于众所周知的wrapper

using (TransactionScope ts = new TransactionScope())
{
    Service<IMyService>.Use(proxy =>
    {
        proxy.DoSomeTransactionalWork();
    });
}

WCF服务方法需要合同定义中的事务并为其隐式投票,抛出FaultException<MyException>

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public bool DoSomeTransactionalWork()
{
    throw new FaultException<MyException>(new MyException(myMessage));
}    

WCF代理收到TransactionAbortedExceptionInnerException设置为null,因此丢失了WCF错误。

try
{
    using (TransactionScope ts = new TransactionScope())
    {
        Service<IMyService>.Use(proxy =>
        {
            proxy.DoSomeTransactionalWork();
        });
    }
}
catch (TransactionAbortedException tae)   
{
    // tae.InnerException is null
}

在类似的情况下,服务方法的合同不要求交易:

public bool DoSomeWork()
{
    throw new FaultException<MyException>(new MyException(myMessage));
}  

,客户端只需通过相同的代理代码调用它,就会收到FaultException<MyException>

try
{
        Service<MyService>.Use(proxy =>
        {
            proxy.DoSomeWork();
        });

}
catch (FaultException<MyException> tae)   
{
    //
}

我错过了什么或是预期的behavior

TIA。

编辑1

客户端代码中的此同步调用完全没有问题。

但是,如果在使用APM时进行错误的异步调用,可能会遇到我描述的行为。请参阅我的回复。

1 个答案:

答案 0 :(得分:0)

同时调用我没有这个问题。

但是,我正在进行无效的异步调用,导致此错误。因为BeginXXX和相应的EndXXX调用可以在不同的线程上执行,所以我创建了一个DependentTransaction:

public class APMState
{
    public IClientChannel Proxy { get; set; }
    public OperationContext Identity { get; set; } 
    public DependentTransaction Transaction { get; set; }   
}

Transaction tx = Transaction.Current;
try
{
    DependentTransaction dtx = tx.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
    // There is no need for a TransactionScope here, the one set by the calling method is used.
    ((IMyService)proxy).BeginDoSomeTransactionalWork(..., new APMState{ Identity = OperationContext.Current, Proxy = proxy, Transaction = dtx});
}
catch (TransactionAbortedException tae)
{
}  

我的EndXXX调用代码不正确:您应该在调用EndXXX方法之前验证事务是否已经中止。如果不这样做,最终会导致TransactionScope构造函数抛出一个TransactionAbortedException。

APMState initialState = ar.AsyncState as APMState;
DependentTransaction  dtx = initialState.Transaction;
if (dtx.TransactionInformation.Status != TransactionStatus.Aborted)
{
    using (TransactionScope scope = new TransactionScope (dtx))
    {
        ae.Result = ((IMyService)initialState.Proxy).EndDoSomeTransactionalWork(ar);
        scope.Complete();
    }

    dtx.Complete();
}
else
{
    log.Error(@" The transaction has aborted :-(");
    log.Debug(@" --> Calling EndDoSomeTransactionalWork on proxy outside a transaction scope to retreive the WCF fault :-)");
    ae.Result = ((IMyService)initialState.Proxy).EndDoSomeTransactionalWork(ar);
}

解决了问题。