Pregunta

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?

¿Fue útil?

Solución

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.

Otros consejos

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
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top