我正在设计一个由多个块组成的长时间运行的Dataflow管道。项目被输入到管道的输入块,最终通过它,并在最后的UI中显示(作为对用户的礼貌 - 管道的真正工作是将处理结果保存到磁盘)。 / p>
管道块内的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;
}
}
但是我在管道中有大约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);
}
};
}
但这似乎有点过分。还有更好的方法吗?
答案 0 :(得分:4)
这是非常有趣的主题。
您可以在链接块时定义过滤器,这意味着您可以将错误结果转移到错误处理块。要做到这一点,块应返回&#34; meta&#34;包含其处理结果和至少包含失败/成功指示符的对象。
在Railroad Oriented Programming中更好地描述了这个想法,其中链中的每个函数处理成功的结果或将失败的结果转移到失败的跟踪&#34;最终记录。
实际上,这意味着您应该在每个块之后添加两个链接:一个具有转移到错误处理块的过滤条件,另一个默认链接转到流程的下一步。
您甚至可以将这两个想法结合起来处理部分失败。部分失败结果将包含故障指示符和有效负载。您可以将结果转移到日志记录块,然后再将其传递到下一步。
我发现很多更容易明确每条消息的状态,而不是通过检查空值,缺失值等来尝试确定其状态。这意味着这些块应该将结果包装在&#34; envelope&#34;包含状态标志,结果和/或任何错误的对象。