0

I have the following scenario :

A WCF Client starts and propagates a transaction to a WCF Service using a TransactionScope.

The client contract is the following :

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

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

}

The proxy used is not generated, it is based on the well known wrapper :

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

The WCF Service method, which requires a transaction in the contract definition and votes implicitly for it, throws a FaultException<MyException>.

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

The WCF proxy receives a TransactionAbortedException with InnerException set to null therefore losing the WCF Fault.

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

In a similar scenario where a Transaction isn't required by the contract for a service method :

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

and the client simply calls it via the same proxy code, a FaultException<MyException> is received.

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

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

Am I missing something or is it the expected behavior ?

TIA.

EDIT 1

There is no problem at all with this synchronous call in client code.

HOWEVER, you may encounter the behavior I describe if you're making an incorrect asynchronous call when working with the APM. See my response.

Community
  • 1
  • 1

1 Answers1

0

Well I do not have this problem with a synchronous call.

However, I was making an invalid asynchronous call leading to this error. Because the BeginXXX and the corresponding EndXXX calls may execute on different threads, I'm creating a 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)
{
}  

My EndXXX calling code was incorrect : You should verify that the transaction is not already aborted before calling the EndXXX method. If you don't you end up with A TransactionAbortedException thrown by the TransactionScope constructor.

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

Problem Solved.