Как получить контекст исключения - PullRequest
1 голос
/ 26 мая 2020

Я использую TaskParallelLibrary DataFlow в сочетании с Try библиотекой, разработанной Стивеном Клири (https://github.com/StephenCleary/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;
}

1 Ответ

1 голос
/ 27 мая 2020

Вы можете использовать структуры ValueTuple<TId, Try<TResult>> в качестве сообщений для конвейера, но может быть немного удобнее создать настраиваемую оболочку класса 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.
    }
});
...