0

我有以下情况:

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

客户合同如下:

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

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

}

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

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

WCF Service 方法需要合约定义中的事务并对其进行隐式投票,它会抛出一个FaultException<MyException>.

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

WCF 代理收到一个TransactionAbortedException设置InnerException为 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>就会收到 a 。

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

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

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

TIA。

编辑 1

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

但是,如果您在使用 APM 时进行了不正确的异步调用,您可能会遇到我描述的行为。看我的回复。

4

1 回答 1

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);
}

问题解决了。

于 2013-07-24T08:34:33.367 回答