Task ContinueWith state propagation

The last couple of weeks I had to dive into the Task Parallel Library (TPL) at work.

We have to use interfaces, which return Tasks, then have to work on the result, but return another task to the caller, like this:

public Task DoWorkWithIntermediateWork()
{
    Task task = OtherComponent.DoWork();
    return task.ContinueWith(t =>
    {
        // Do something with the result
    });
}

But the given action shall only execute, if the predecessor task completes successfully. If not the state of the predecessor task shall be propagated to the task, which is returned by the method.

The following helper method may help, if you have the same problem:

/// <summary>
/// Will only execute the given continuationAction, if the given predecessor task completes successfully.
/// The states Faulted and Canceled are propagated to the returned task.
/// </summary>
/// <param name="predecessor">Predecessor task.</param>
/// <param name="continuationAction">Action to Execute, if predecessor completes successfully</param>
/// <returns></returns>
public static Task StatePropagatingContinueWith(Task predecessor, Action<Task> continuationAction)
{
    var taskSource = new TaskCompletionSource<object>();

    predecessor.ContinueWith(t =>
    {
        if (t.IsCanceled)
        {
            taskSource.SetCanceled();
            return;
        }
        if (t.IsFaulted)
        {
            taskSource.SetException(t.Exception.InnerExceptions);
            return;
        }

        try
        {
            continuationAction(predecessor);
            taskSource.SetResult(null);
        }
        catch (Exception e)
        {
            taskSource.SetException(e);
        }
    });

    return taskSource.Task;
}

Here instead of directly appending the continuation action at the predecessor task, there is a TaskCompletionSource, which configures a task dependent on the state of the predecessor task.
The action is only executed, if the predecessor succeeds.

I recommend to extract an interface and make an alternative implementation for easy unit testing. Zwinkerndes Smiley