Question

I am using the TransformBlock from the TPL Dataflow library, and I have realized that when a exception is thrown during the transform, I get a generic exception in the "Receive" method, but no mention to the original one.

In this code:

Func<Int32, Task<String>> transformer = async i => { await Task.Yield(); throw new ArgumentException("whatever error"); };
TransformBlock<Int32, String> transform = new TransformBlock<int, string>(transformer);
transform.Post(1);

try
{
    var x = await transform.ReceiveAsync();
}
catch (Exception ex)
{
    // catch
}

The exception ex contains:

System.InvalidOperationException was caught
  HResult=-2146233079
  Message=The source completed without providing data to receive.
  Source=System.Threading.Tasks.Dataflow
  StackTrace:
       at System.Threading.Tasks.Dataflow.Internal.Common.InitializeStackTrace(Exception exception)
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at DoubleQueueTest.Program.<testwhatever>d__5.MoveNext() in c:\Users\vtortola\Documents\Visual Studio 2013\Projects\DoubleQueueTest\DoubleQueueTest\Program.cs:line 43
  InnerException: 

No mention to the original exception type or its message. Is there a way to force it to throw the original one? or at least, use it as inner exception?

Était-ce utile?

La solution

You're seeing an exception from ReceiveAsync. The InvalidOperationException is expected behavior.

If you want to detect or respond to block faults, then await the IDataflowBlock.Completion property.

Autres conseils

I am adding this response just for completion. As @Stephen Clearly stated, the transform will fault if one of the items faults. So if your transform should not stop because a single item faults, I have used this approach:

Create a small class that represents the operation result:

 class TransformResult<T>
 {
     public T Result { get; set; }
     public ExceptionDispatchInfo Error { get; set; }
 }

If there is an exception, capture it and return the result:

Func<Int32, Task<TransformResult<String>>> transformer = async i => 
{ 
    await Task.Yield();
    try
    {
        // do whatever

        throw new ArgumentException("whatever error");
    }
    catch (Exception ex)
    {
        return new TransformResult<String>() { Error = ExceptionDispatchInfo.Capture(ex) };
    }
};

And when awaiting the transform result, if the result contains an error... throw it:

var transform = new TransformBlock<int, TransformResult<String>>(transformer);
transform.Post(1);

try
{
    var x = await transform.ReceiveAsync();
    if (x.Error != null)
        x.Error.Throw();
}
catch (Exception ex)
{
    // catch
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top