Philiprehberger.TaskDependencyRunner
0.3.1
dotnet add package Philiprehberger.TaskDependencyRunner --version 0.3.1
NuGet\Install-Package Philiprehberger.TaskDependencyRunner -Version 0.3.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Philiprehberger.TaskDependencyRunner" Version="0.3.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Philiprehberger.TaskDependencyRunner" Version="0.3.1" />
<PackageReference Include="Philiprehberger.TaskDependencyRunner" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Philiprehberger.TaskDependencyRunner --version 0.3.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Philiprehberger.TaskDependencyRunner, 0.3.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Philiprehberger.TaskDependencyRunner@0.3.1
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Philiprehberger.TaskDependencyRunner&version=0.3.1
#tool nuget:?package=Philiprehberger.TaskDependencyRunner&version=0.3.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
Philiprehberger.TaskDependencyRunner
Lightweight task runner with dependency graph resolution and parallel execution.
Installation
dotnet add package Philiprehberger.TaskDependencyRunner
Usage
using Philiprehberger.TaskDependencyRunner;
var graph = new TaskGraph()
.Add("clean", () => Console.WriteLine("Cleaning..."))
.Add("restore", () => Console.WriteLine("Restoring packages..."), "clean")
.Add("build", () => Console.WriteLine("Building..."), "restore")
.Add("test", () => Console.WriteLine("Running tests..."), "build")
.Add("pack", () => Console.WriteLine("Packing..."), "build");
await graph.RunAsync();
Typed task results
Tasks can produce results that downstream tasks consume via ITaskContext:
using Philiprehberger.TaskDependencyRunner;
var graph = new TaskGraph()
.Add<int>("fetch-count", _ => Task.FromResult(42))
.Add<string>("format", ctx =>
{
var count = ctx.GetResult<int>("fetch-count");
return Task.FromResult($"Total: {count}");
}, "fetch-count")
.Add("print", (ctx) =>
{
Console.WriteLine(ctx.GetResult<string>("format"));
return Task.CompletedTask;
}, "format");
await graph.RunAsync();
// Access results after execution
var formatted = (string)graph.TaskResults["format"]!;
Per-task timeout
Set a timeout on individual tasks. If the timeout elapses, a TaskTimeoutException is thrown:
var graph = new TaskGraph()
.Add("fast", () => Console.WriteLine("Done"))
.Add("slow", async () => await Task.Delay(10_000), timeout: TimeSpan.FromSeconds(2));
try
{
await graph.RunAsync();
}
catch (TaskTimeoutException ex)
{
Console.WriteLine($"{ex.TaskName} timed out");
}
Progress reporting
Track execution progress with the IProgressReporter interface:
var graph = new TaskGraph
{
ProgressReporter = new ConsoleProgressReporter()
};
graph
.Add("clean", () => { })
.Add("build", () => { }, "clean")
.Add("test", () => { }, "build");
await graph.RunAsync();
// Output:
// [START] clean
// [DONE] clean (1ms)
// [START] build
// [DONE] build (0ms)
// [START] test
// [DONE] test (0ms)
Dry-run mode
Validate the graph and inspect the execution plan without running any tasks:
var graph = new TaskGraph()
.Add("a", () => { })
.Add("b", () => { })
.Add("c", () => { }, "a", "b")
.Add("d", () => { }, "c");
var plan = graph.DryRun();
foreach (var (batch, i) in plan.Batches.Select((b, i) => (b, i)))
Console.WriteLine($"Batch {i}: {string.Join(", ", batch)}");
// Batch 0: a, b
// Batch 1: c
// Batch 2: d
Max concurrency
Limit how many tasks run in parallel:
var graph = new TaskGraph { MaxConcurrency = 2 };
graph
.Add("a", async () => await Task.Delay(100))
.Add("b", async () => await Task.Delay(100))
.Add("c", async () => await Task.Delay(100));
await graph.RunAsync();
Error handling
// Circular dependency
var bad = new TaskGraph()
.Add("a", () => { }, "b")
.Add("b", () => { }, "a");
// Throws CircularDependencyException
// Missing dependency
var missing = new TaskGraph()
.Add("a", () => { }, "nonexistent");
// Throws MissingDependencyException
API
TaskGraph
| Member | Description |
|---|---|
Add(name, Action, params string[]) |
Register a synchronous task |
Add(name, Func<Task>, params string[]) |
Register an async task |
Add(name, Func<ITaskContext, Task>, params string[]) |
Register an async task with context access |
Add<TResult>(name, Func<ITaskContext, Task<TResult>>, params string[]) |
Register a typed result task |
Add(name, action, TimeSpan?, params string[]) |
Register a task with timeout (sync or async) |
GetExecutionOrder() |
Return names in topological order |
RunAsync(CancellationToken) |
Execute all tasks; independent tasks run in parallel |
DryRun() |
Validate graph and return ExecutionPlan without executing |
MaxConcurrency |
Max parallel tasks (0 = unlimited, default) |
OnTaskCompleted |
Action<string, int, int>? callback (taskName, completedCount, totalCount) |
ProgressReporter |
IProgressReporter? for detailed start/complete/fail events |
TaskResults |
IReadOnlyDictionary<string, object?> of typed task results |
ITaskContext
| Method | Description |
|---|---|
GetResult<T>(taskName) |
Retrieve a dependency's typed result |
HasResult(taskName) |
Check whether a result exists for the given task |
IProgressReporter
| Method | Description |
|---|---|
OnTaskStarted(name) |
Called when a task begins execution |
OnTaskCompleted(name, elapsed) |
Called when a task completes successfully |
OnTaskFailed(name, exception) |
Called when a task fails |
ExecutionPlan
| Property | Type | Description |
|---|---|---|
Batches |
IReadOnlyList<IReadOnlyList<string>> |
Ordered batches of parallelizable tasks |
Order |
IReadOnlyList<string> |
Flat topological order |
TaskCount |
int |
Total number of tasks |
Exceptions
| Type | Description |
|---|---|
CircularDependencyException |
The graph contains a cycle |
MissingDependencyException |
A task depends on an unregistered name |
TaskTimeoutException |
A task exceeded its configured timeout (TaskName property) |
Development
dotnet build src/Philiprehberger.TaskDependencyRunner.csproj --configuration Release
Support
If you find this project useful:
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.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.