使用TransformBlock捕获原始异常

3
我正在使用TPL Dataflow库中的TransformBlock,但是我发现在转换过程中如果出现异常,在“Receive”方法中会收到一个通用的异常,但没有提及原始异常。
在这段代码中:
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
}

异常ex包含:

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: 

没有提及原始异常类型或其消息。是否有一种方法可以强制它抛出原始异常?或者至少将其用作内部异常?
2个回答

5

您看到了一个来自ReceiveAsync的异常。 InvalidOperationException是预期的行为。

如果您想检测或响应块错误,请await IDataflowBlock.Completion属性。


但如果转换永远不会结束呢?或者只有一些项目失败了呢? - vtortola
如果任何项目的转换失败,该块将出现故障,并且不会接受任何其他项目。如果转换永远不会结束,那么这就是一个错误 - 就像一个无限循环。 - Stephen Cleary
我想表达的是这是一个持续的转换,因为新的项目会不断到来,而且转换不应该因为其中一个失败而停止。但我明白了您的意思。 - vtortola
如果您不希望代码块出现故障,那么您应该在转换委托中捕获任何异常。 - Stephen Cleary

0
我只是为了完整性而添加这个回复。正如@Stephen所述,如果其中一个项目出现故障,转换将会出错。因此,如果您的转换不应该因为单个项目出现故障而停止,我已经采用了以下方法:
创建一个表示操作结果的小类:
 class TransformResult<T>
 {
     public T Result { get; set; }
     public ExceptionDispatchInfo Error { get; set; }
 }

如果出现异常,请捕获它并返回结果:

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

当等待转换结果时,如果结果包含错误...抛出它:

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接