Dataflow管道中的全局每块错误处理

Bug*_*ter 10 c# error-handling tpl-dataflow

我正在设计一个由多个块组成的长时间运行的Dataflow管道.项目被输入到管道的输入块,最终通过它,并在最后的UI中显示(作为对用户的礼貌 - 管道的真正工作是将处理结果保存到磁盘).

由于各种原因(输入错误,网络故障,计算错误等),管道块内的lambda函数可能会抛出异常.在这种情况下,我不想破坏整个管道,而是想要触发违规项目,并在"错误"下的UI中显示它.

最好的方法是什么?我知道我可以在try/catch中包装每个lambda函数:

var errorLoggingBlock = new ActionBlock<Tuple<WorkItem, Exception>>(...)

var workerBlock = new TransformBlock<WorkItem, WorkItem>(item => 
{
    try {
        return DoStuff(item);
    } catch (Exception ex) {
        errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我在管道中有大约10个块,并且将代码复制/粘贴到每个块中似乎很愚蠢.此外,我不喜欢返回null的想法,因为现在所有下游块都必须检查它.

我的下一个最好的想法是创建一个函数,返回一个为我做包装的lambda:

  private Func<TArg, TResult> HandleErrors<TArg, TResult>(Func<TArg, TResult> f) where TArg:WorkItem
  {
     return arg =>
     {
        try {
           return f(arg);
        } catch (Exception ex) {
           errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
           return default(TResult);
        }
     };
  }
Run Code Online (Sandbox Code Playgroud)

但这似乎有点过分.有没有更好的办法 ?

Pan*_*vos 4

这是一个非常有趣的话题。

您可以在链接块时定义过滤器,这意味着您可以将错误结果转移到错误处理块。为此,块应该返回“元”对象,其中包含其处理结果和至少一个失败/成功指示器。

这个想法在面向铁路的编程中得到了更好的描述,其中链中的每个函数处理成功的结果或将失败的结果转移到“失败的轨道”以进行最终记录。

实际上,这意味着您应该在每个块之后添加两个链接:一个具有转移到错误处理块的过滤条件,另一个是转到流程中下一步的默认链接。

您甚至可以结合这两种想法来处理部分故障。部分失败结果将包含失败指示符和有效负载。您可以将结果转移到日志块,然后再将其传递到下一步。

我发现明确每条消息的状态比尝试通过检查 null、缺失值等来确定其状态要容易得多。这意味着块应该将其结果包装在包含状态标志的“信封”对象中结果和/或任何错误。