如何获取异常的上下文

How to get context of Exception

我正在使用 TaskParallelLibrary DataFlow 结合由 Stephen Cleary (https://github.com/StephenCleary/Try) 设计的 Try 库来实现所谓的 "railroad programming" 所以我可以通过 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;
}

您可以使用 ValueTuple<TId, Try<TResult>> 结构作为管道的消息,但创建 Try class 的自定义包装器可能会稍微方便一些,它也包含 id。由于此包装器将有两个类型参数,因此也可以将其命名为 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.
    }
});