RLC.TaskChaining
2.1.0
See the version list below for details.
dotnet add package RLC.TaskChaining --version 2.1.0
NuGet\Install-Package RLC.TaskChaining -Version 2.1.0
<PackageReference Include="RLC.TaskChaining" Version="2.1.0" />
<PackageVersion Include="RLC.TaskChaining" Version="2.1.0" />
<PackageReference Include="RLC.TaskChaining" />
paket add RLC.TaskChaining --version 2.1.0
#r "nuget: RLC.TaskChaining, 2.1.0"
#:package RLC.TaskChaining@2.1.0
#addin nuget:?package=RLC.TaskChaining&version=2.1.0
#tool nuget:?package=RLC.TaskChaining&version=2.1.0
RLC.TaskChaining
Monadic-style chaining for C# Tasks.
Rationale
Asynchronous code (particularly in C#) typically relies on using the async/await feature introduced in C# 5.0. This has a lot of benefits, but it unfortunately tends to push code into an imperative style. This library aims to make writing asychronous functional code easier, cleaner, and less error-prone using extensions to System.Threading.Tasks.
Installation
Install RLC.TaskChaining as a NuGet package via an IDE package manager or using the command-line instructions at nuget.org.
API
Monadic Functions
Monads are usually portrayed as a lot more complicated than they really have to be. See MONADS.md for a brief tutorial. Note that this library contains both standard monadic functions (such as ResultMap(fmap), Bind, etc) and bluebird Promise-style functions (Then, Catch, Tap, etc). Some scenarios will lend themselves to one over the other.
Chaining
Then
Once a Task<T> has been created, successive operations can be chained using the Then method.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult("https://www.google.com") // Task<string>
.Then(client.GetAsync) // Task<HttpResponseMessage>
.Then(response => response.StatusCode); // Task<System.Net.HttpStatusCode>
Catch
When a Task<T> enters a faulted state, the Catch method can be used to return the Task<T> to a non-faulted state.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult("not-a-url") // Task<string>
.Then(client.GetAsync) // Task<HttpResponseMessage> but FAULTED
.Catch(exception => exception.Message) // Task<string> and NOT FAULTED
.Then(message => message.Length) // Task<int>
Note that it's entirely possible for a Catch method to cause the Task<T> to remain in a faulted state, e.g. if you only wanted to recover into a non-faulted state if a particular exception type occurred.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult("not-a-url") // Task<string>
.Then(client.GetAsync) // Task<HttpResponseMessage> but FAULTED
.Catch(exception => exception is NullReferenceException
? exception.Message
: Task.FromException(exception)) // Task<string> but STILL FAULTED if anything other than NullReferenceException occurred
.Then(message => message.Length) // Task<int>
IfFulfilled/IfFaulted/Tap
The IfFulfilled and IfFaulted methods can be used to perform side effects such as logging when the Task<T> is in the fulfilled or faulted state, respectively.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult("https://www.google.com/")
.Then(client.GetAsync)
.IfFulfilled(response => _logger.LogDebug("Got response {Response}", response)
.Then(response => response.StatusCode);
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult("not-a-url")
.Then(client.GetAsync)
.IfFaulted(exception => _logger.LogException(exception, "Failed to get URL")
.Catch(exception => exception.Message)
.Then(message => message.Length);
The Tap method takes both an onFulfilled and onRejected Action in the event that you want to perform some side effect on both sides of the Task at a single time.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
Task.FromResult(someExternalUrl)
.Then(client.GetAsync)
.Tap(
response => _logger.LogDebug("Got response {Response}", response),
exception => _logger.LogException(exception, "Failed to get URL")
)
Retry
Task.Retry can be used to automatically retry a function. The RetryOptions type holds the retry interval, backoff rate, maximum attempt count, an optional Action to perform when a retry is about to happen, and a Predicate<Exception> that is used to decide whether or not a retry should be performed based on the Exception that occurred during the last execution.
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever
ILogger logger;
RetryOptions options = new (
3,
TimeSpan.FromMilliseconds(1000),
2,
(attemptCount, duration, exception) => logger.LogError(exception, $"Starting retry {attemptCount} after {duration.TotalMilliseconds} milliseconds"),
exception => exception is NullReferenceException ? true : false // Only NullReferenceExceptions will trigger retries, other exceptions will fall through
);
Task.FromResult(someExternalUrl)
.Retry(client.GetAsync, options)
If the RetryOptions parameter is not passed, the default values (3 attempts, 1000ms duration, backoff rate of 2) are used.
Static Methods
There are some convenience methods on TaskExtras that are useful when transitioning between the fulfilled and faulted states.
RejectIf
RejectIf can be used to transition into a faulted state based on some Predicate<T>.
Task.FromResult(1)
.Then(TaskExtras.RejectIf(
value => value % 2 == 0,
value => new Exception($"{nameof(value)} was not even")
));
ResolveIf
ResolveIf can be used to transition from a faulted state to a fulfilled state based on some Predicate<Exception>.
Task.FromException<int>(new ArgumentException())
.Catch(TaskExtras.ResolveIf(
exception => exception is ArgumentException,
exception => exception.Message.Length
))
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.20.0 | 885 | 7/8/2025 |
| 2.19.2 | 2,074 | 3/27/2025 |
| 2.19.0 | 308 | 3/6/2025 |
| 2.18.5 | 1,435 | 11/25/2024 |
| 2.18.4 | 189 | 11/22/2024 |
| 2.18.3 | 184 | 11/22/2024 |
| 2.18.2 | 195 | 11/22/2024 |
| 2.18.1 | 1,127 | 7/26/2024 |
| 2.16.1 | 2,106 | 4/20/2024 |
| 2.16.0 | 973 | 12/18/2023 |
| 2.15.0 | 2,312 | 11/1/2023 |
| 2.14.0 | 2,166 | 5/14/2023 |
| 2.13.0 | 590 | 5/12/2023 |
| 2.12.0 | 554 | 5/12/2023 |
| 2.11.0 | 9,414 | 3/12/2023 |
| 2.10.0 | 872 | 12/29/2022 |
| 2.9.0 | 916 | 9/22/2022 |
| 2.8.0 | 848 | 9/21/2022 |
| 2.7.0 | 1,137 | 6/18/2022 |
| 2.6.0 | 895 | 6/17/2022 |
| 2.5.0 | 879 | 6/16/2022 |
| 2.4.0 | 947 | 6/10/2022 |
| 2.3.0 | 891 | 6/9/2022 |
| 2.2.0 | 865 | 5/20/2022 |
| 2.1.2 | 890 | 5/17/2022 |
| 2.1.1 | 915 | 5/13/2022 |
| 2.1.0 | 854 | 5/13/2022 |
| 2.0.0 | 866 | 5/1/2022 |
| 1.0.0 | 879 | 4/26/2022 |
| 0.3.1 | 836 | 4/26/2022 |
| 0.3.0 | 852 | 4/26/2022 |
| 0.2.0 | 903 | 4/24/2022 |
| 0.1.1 | 879 | 4/18/2022 |
| 0.1.0 | 941 | 4/18/2022 |