【问题标题】:How to get context of Exception如何获取异常的上下文
【发布时间】:2020-05-26 13:33:10
【问题描述】:

我正在使用 TaskParallelLibrary DataFlow 与 Stephen Cleary (https://github.com/StephenCleary/Try) 设计的 Try 库相结合来实现所谓的“铁路编程”,因此我可以将 Exception 数据传递到管道中。我想知道在ActionBlock 中是否有可能获得一些关于什么或(在我的情况下)究竟是哪个项目导致Exception 的上下文? 这是小示例代码:

public async Task TestRailroadException(List<int> constructIds)
{
    var downloadBlock = new TransformBlock<int, Try<int>>(
        construct => Try.Create(() =>
    {
        //ThisMethodMyThrowException();
        return 1;
    }));

    var processBlock = new TransformBlock<Try<int>, Try<int>>(
        construct => construct.Map(value =>
    {
        //ThisMethodMyAlsoThrowException();
        return 1;
    }));

    var resultsBlock = new ActionBlock<Try<int>>(construct =>
    {
        if (construct.IsException)
        {
            var type = construct.Exception.GetType();
            //Here it would be nice to know which item(id) was faulted.
        }
    });
    downloadBlock.LinkTo(processBlock, new DataflowLinkOptions
        { PropagateCompletion = true });
    processBlock.LinkTo(resultsBlock, new DataflowLinkOptions
        { PropagateCompletion = true });
    foreach (var constructId in constructIds)
    {
        await downloadBlock.SendAsync(constructId);
    }

    downloadBlock.Complete();
    await resultsBlock.Completion;
}

【问题讨论】:

  • 看看这个:AsyncLocal values not correct with TPL Dataflow。 TL;DR AsyncLocal 类不能用于将环境上下文数据与每条消息一起传递。因此,除了使用元组或自定义类或结构作为消息手动将上下文数据从一个块传递到另一个块之外,别无选择。
  • @Theodor。谢谢你的答案。你认为在每个块中捕获错误,添加带有错误项目 id 的 exception.Data,然后重新抛出它们以在 resultsBlock 中再次捕获它们是个好主意吗?这样我就可以知道是谁造成了exception,而我不必在每个块中重复所有catch 逻辑。这不是很漂亮的解决方案,但它可以完成工作。
  • 是的,没关系。但是使用 Stephen Cleary 的 Try 库可能会更干净。它在技术上也更优越,除非您愿意了解 ExceptionDispatchInfo 类,并使用它而不是裸露的 Exception 来存储异常。 :-)
  • var downloadBlock = new TransformBlock&lt;int, (int, Try&lt;int&gt;)&gt; 怎么样?结果是一个ValueTuple,有两个成员,即原始项目及其结果,结果包装在Try 中。然后继续将原始项目从一个块传递到另一个块,使用元组作为TInputTOutput
  • @Theodor 啊!但是当然!如此简单,却又精彩!谢谢!如果您将此作为答案与一个小样本一起发布,我绝对会接受它!

标签: c# task-parallel-library pipeline tpl-dataflow


【解决方案1】:

您可以使用ValueTuple&lt;TId, Try&lt;TResult&gt;&gt; 结构体作为管道的消息,但创建一个同时包含 id 的 Try 类的自定义包装器可能会更方便一些。由于此包装器将有两个类型参数,因此也可以将其命名为 Try

public readonly struct Try<TId, TResult>
{
    public static Try<TId, TResult> Create(TId id, Func<TResult> func)
        => new Try<TId, TResult>(id, Try.Create(func));

    public static async Task<Try<TId, TResult>> Create(TId id,
        Func<Task<TResult>> func)
        => new Try<TId, TResult>(id, await Try.Create(func).ConfigureAwait(false));

    public readonly TId Id { get; }
    public readonly Try<TResult> Result { get; }

    private Try(TId id, Try<TResult> result) { Id = id; Result = result; }

    public Try<TId, TNewResult> Map<TNewResult>(Func<TResult, TNewResult> func)
        => new Try<TId, TNewResult>(Id, Result.Map(func));

    public async Task<Try<TId, TNewResult>> Map<TNewResult>(
        Func<TResult, Task<TNewResult>> func)
        => new Try<TId, TNewResult>(Id, await Result.Map(func).ConfigureAwait(false));
}

然后你可以像这样使用它:

var downloadBlock = new TransformBlock<int, Try<int, int>>(
    construct => Try<int, int>.Create(construct, async () =>
{
    await SometimesThrowsAsync();
    return 1;
}));

var processBlock = new TransformBlock<Try<int, int>, Try<int, int>>(
    construct => construct.Map(async value =>
{
    await SometimesThrowsAsync();
    return 1;
}));

var resultsBlock = new ActionBlock<Try<int, int>>(construct =>
{
    if (construct.Result.IsException)
    {
        var type = construct.Result.Exception.GetType();
        //Log that the {construct.Id} has failed.
    }
});

【讨论】:

  • 非常好的包装!但是,如果ThisMethodMyThrowException 是应该等待的异步方法,我正在努力实现这一点。我只是无法让它工作。你能分享一些技巧吗?
  • @niks 我用异步友好的方法更新了Try&lt;TId, TResult&gt;。它现在应该可以与异步 TPL 数据流块集成。
  • 再次感谢您!我花了一些时间来掌握它,TPLasync 东西不是我最强的一面……我自己无法想出这样的解决方案!我再次对这个网站的运作方式以及来自世界各地的人们如何愿意花时间帮助他人感到惊讶:)Efcharistó!
  • @niks Ευχαρίστησή μου 我的朋友。异步/等待太棒了! ?
猜你喜欢
  • 2010-12-21
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 2011-01-13
  • 2019-01-20
相关资源
最近更新 更多