WorkflowFramework 0.15.0
dotnet add package WorkflowFramework --version 0.15.0
NuGet\Install-Package WorkflowFramework -Version 0.15.0
<PackageReference Include="WorkflowFramework" Version="0.15.0" />
<PackageVersion Include="WorkflowFramework" Version="0.15.0" />
<PackageReference Include="WorkflowFramework" />
paket add WorkflowFramework --version 0.15.0
#r "nuget: WorkflowFramework, 0.15.0"
#:package WorkflowFramework@0.15.0
#addin nuget:?package=WorkflowFramework&version=0.15.0
#tool nuget:?package=WorkflowFramework&version=0.15.0
WorkflowFramework
A fluent, extensible workflow/pipeline engine for .NET with async-first design, middleware, branching, parallel execution, saga/compensation, and rich extensibility.
Features
- Fluent Builder API — chain steps naturally with a clean DSL
- Strongly-Typed Context — type-safe data flowing through your pipeline
- Conditional Branching —
If/Then/Elsefor decision-based workflows - Parallel Execution — run steps concurrently with
Parallel() - Middleware Pipeline — logging, retry, timing, tracing, and custom interceptors
- Saga/Compensation — automatic rollback on failure
- Event Hooks —
OnStepStarted,OnStepCompleted,OnWorkflowFailed, etc. - Persistence/Checkpointing — save and resume long-running workflows
- DI Integration — first-class Microsoft.Extensions.DependencyInjection support
- OpenTelemetry — built-in tracing and timing diagnostics
- Polly Integration — resilience policies via Polly v8
- Multi-targeting — netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0
- Looping —
ForEach,While,DoWhile,Retryfor iteration patterns - Error Handling DSL —
Try/Catch/Finallyblocks in your workflow - Sub-Workflows — compose workflows from smaller workflows
- Typed Pipelines —
IPipelineStep<TIn, TOut>with chained input/output types - Workflow Registry — register and resolve workflows by name with versioning
- Step Attributes —
[StepName],[StepTimeout],[StepRetry],[StepOrder] - Validation —
IWorkflowValidator/IStepValidatorfor pre-execution validation - Visualization — export workflows to Mermaid and DOT/Graphviz diagrams
- Scheduling — cron expressions and delayed execution
- Approval Steps — human interaction with
IApprovalService - Audit Trail —
AuditMiddlewarewithIAuditStore - Testing Utilities —
WorkflowTestHarness,FakeStep,StepTestBuilder - Configuration DSL — define workflows in JSON (YAML coming soon)
- SQLite Persistence — durable state store via SQLite
Quick Start
Install
dotnet add package WorkflowFramework
Define Steps
public class ValidateInput : IStep
{
public string Name => "ValidateInput";
public Task ExecuteAsync(IWorkflowContext context)
{
// validation logic
return Task.CompletedTask;
}
}
public class ProcessData : IStep
{
public string Name => "ProcessData";
public Task ExecuteAsync(IWorkflowContext context)
{
// processing logic
return Task.CompletedTask;
}
}
Build & Execute
var workflow = Workflow.Create("MyWorkflow")
.Step<ValidateInput>()
.Step<ProcessData>()
.Step("SaveResult", ctx =>
{
Console.WriteLine("Saved!");
return Task.CompletedTask;
})
.Build();
var result = await workflow.ExecuteAsync(new WorkflowContext());
Console.WriteLine(result.Status); // Completed
Typed Workflows
public class OrderData
{
public string OrderId { get; set; } = "";
public decimal Total { get; set; }
public bool IsValid { get; set; }
}
public class ValidateOrder : IStep<OrderData>
{
public string Name => "ValidateOrder";
public Task ExecuteAsync(IWorkflowContext<OrderData> context)
{
context.Data.IsValid = context.Data.Total > 0;
return Task.CompletedTask;
}
}
var workflow = Workflow.Create<OrderData>("OrderPipeline")
.Step(new ValidateOrder())
.If(ctx => ctx.Data.IsValid)
.Then(new ProcessOrder())
.Else(new RejectOrder())
.Build();
var result = await workflow.ExecuteAsync(
new WorkflowContext<OrderData>(new OrderData { OrderId = "ORD-1", Total = 99.99m }));
Console.WriteLine(result.Data.IsValid); // true
Conditional Branching
Workflow.Create()
.If(ctx => someCondition)
.Then<ProcessStep>()
.Else<RejectStep>()
.Step<FinalStep>()
.Build();
Parallel Execution
Workflow.Create()
.Parallel(p => p
.Step<SendEmail>()
.Step<SendSms>()
.Step<UpdateDashboard>())
.Build();
Middleware
public class LoggingMiddleware : IWorkflowMiddleware
{
public async Task InvokeAsync(IWorkflowContext context, IStep step, StepDelegate next)
{
Console.WriteLine($"Starting: {step.Name}");
await next(context);
Console.WriteLine($"Completed: {step.Name}");
}
}
Workflow.Create()
.Use<LoggingMiddleware>()
.Step<MyStep>()
.Build();
Saga/Compensation
public class DebitAccount : ICompensatingStep
{
public string Name => "DebitAccount";
public Task ExecuteAsync(IWorkflowContext context) { /* debit */ return Task.CompletedTask; }
public Task CompensateAsync(IWorkflowContext context) { /* credit back */ return Task.CompletedTask; }
}
Workflow.Create()
.WithCompensation()
.Step(new DebitAccount())
.Step(new CreditAccount())
.Build();
Event Hooks
Workflow.Create()
.WithEvents(new MyEventHandler())
.Step<MyStep>()
.Build();
public class MyEventHandler : WorkflowEventsBase
{
public override Task OnStepStartedAsync(IWorkflowContext ctx, IStep step)
{
Console.WriteLine($"Step started: {step.Name}");
return Task.CompletedTask;
}
}
Architecture
graph TD
A[Workflow.Create] --> B[IWorkflowBuilder]
B --> C[Add Steps]
B --> D[Add Middleware]
B --> E[Add Events]
B --> F[Build]
F --> G[WorkflowEngine]
G --> H{Execute Steps}
H --> I[Middleware Pipeline]
I --> J[Step.ExecuteAsync]
J --> K[Context / Properties]
H --> L{Compensation?}
L -->|Yes| M[Reverse Compensate]
H --> N[WorkflowResult]
Why WorkflowFramework?
- Zero dependencies in core — the core package has no external dependencies
- Multi-target — supports netstandard2.0 through net10.0
- Async-first — every API is async from the ground up
- Composable — middleware, events, sub-workflows, typed pipelines
- Testable — built-in test harness, fake steps, and assertions
- Production-ready — persistence, scheduling, distributed locking, health checks
- Extensible — clean interfaces for custom steps, middleware, persistence, and more
Extensions
| Package | Description |
|---|---|
WorkflowFramework |
Core abstractions + fluent builder |
WorkflowFramework.Extensions.DependencyInjection |
Microsoft DI integration |
WorkflowFramework.Extensions.Polly |
Polly resilience policies |
WorkflowFramework.Extensions.Persistence |
Checkpoint/state persistence abstractions |
WorkflowFramework.Extensions.Persistence.InMemory |
In-memory state store |
WorkflowFramework.Extensions.Diagnostics |
OpenTelemetry tracing + timing |
WorkflowFramework.Generators |
Source generators for step discovery |
WorkflowFramework.Extensions.Configuration |
JSON/YAML workflow definitions |
WorkflowFramework.Extensions.Scheduling |
Cron scheduling, approvals, delayed execution |
WorkflowFramework.Extensions.Visualization |
Mermaid + DOT diagram export |
WorkflowFramework.Extensions.Reactive |
Async streams / IAsyncEnumerable support |
WorkflowFramework.Extensions.Persistence.Sqlite |
SQLite state store |
WorkflowFramework.Testing |
Test harness, fake steps, event capture |
WorkflowFramework.Extensions.Persistence.EntityFramework |
EF Core state store |
WorkflowFramework.Extensions.Distributed |
Distributed locking and queuing abstractions |
WorkflowFramework.Extensions.Distributed.Redis |
Redis lock and queue implementations |
WorkflowFramework.Extensions.Hosting |
ASP.NET Core hosting integration + health checks |
WorkflowFramework.Extensions.Http |
HTTP request steps with fluent builder |
WorkflowFramework.Analyzers |
Roslyn analyzers for common mistakes |
Polly Integration
using WorkflowFramework.Extensions.Polly;
Workflow.Create()
.UseResilience(builder => builder
.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 }))
.Step<UnreliableStep>()
.Build();
Diagnostics
using WorkflowFramework.Extensions.Diagnostics;
Workflow.Create()
.Use<TracingMiddleware>() // OpenTelemetry spans
.Use<TimingMiddleware>() // Step timing
.Step<MyStep>()
.Build();
Persistence
using WorkflowFramework.Extensions.Persistence;
using WorkflowFramework.Extensions.Persistence.InMemory;
var store = new InMemoryWorkflowStateStore();
Workflow.Create()
.Use(new CheckpointMiddleware(store))
.Step<LongRunningStep>()
.Build();
Dependency Injection
using WorkflowFramework.Extensions.DependencyInjection;
services.AddWorkflowFramework();
services.AddStep<ValidateInput>();
services.AddWorkflowMiddleware<LoggingMiddleware>();
Looping & Iteration
using WorkflowFramework.Builder;
Workflow.Create()
.ForEach<string>(
ctx => (List<string>)ctx.Properties["Items"]!,
body => body.Step("Process", ctx =>
{
var item = (string)ctx.Properties["ForEach.Current"]!;
Console.WriteLine($"Processing: {item}");
return Task.CompletedTask;
}))
.Build();
// While loop
Workflow.Create()
.While(ctx => (int)ctx.Properties["Count"]! < 10,
body => body.Step("Increment", ctx =>
{
ctx.Properties["Count"] = (int)ctx.Properties["Count"]! + 1;
return Task.CompletedTask;
}))
.Build();
// Retry group
Workflow.Create()
.Retry(body => body.Step<FlakyStep>(), maxAttempts: 3)
.Build();
Try/Catch/Finally
Workflow.Create()
.Try(body => body.Step<RiskyStep>())
.Catch<InvalidOperationException>((ctx, ex) =>
{
Console.WriteLine($"Caught: {ex.Message}");
return Task.CompletedTask;
})
.Finally(body => body.Step("Cleanup", _ => { /* cleanup */ return Task.CompletedTask; }))
.Build();
Sub-Workflows
var validation = Workflow.Create("Validation")
.Step<ValidateInput>()
.Step<ValidatePermissions>()
.Build();
var main = Workflow.Create("Main")
.SubWorkflow(validation)
.Step<ProcessData>()
.Build();
Typed Pipeline
using WorkflowFramework.Pipeline;
var pipeline = Pipeline.Create<string>()
.Pipe<int>((s, ct) => Task.FromResult(int.Parse(s)))
.Pipe<string>((n, ct) => Task.FromResult($"Value: {n * 2}"))
.Build();
var result = await pipeline("21", CancellationToken.None); // "Value: 42"
Workflow Registry & Versioning
using WorkflowFramework.Registry;
using WorkflowFramework.Versioning;
var registry = new WorkflowRegistry();
registry.Register("OrderProcessing", () => BuildOrderWorkflow());
var runner = new WorkflowRunner(registry);
var result = await runner.RunAsync("OrderProcessing", context);
// Versioned workflows
var versionedRegistry = new VersionedWorkflowRegistry();
versionedRegistry.Register("OrderProcessing", 1, () => BuildV1());
versionedRegistry.Register("OrderProcessing", 2, () => BuildV2());
var latest = versionedRegistry.Resolve("OrderProcessing"); // Returns v2
Visualization
using WorkflowFramework.Extensions.Visualization;
var workflow = Workflow.Create("MyWorkflow")
.Step<StepA>()
.Step<StepB>()
.Build();
Console.WriteLine(workflow.ToMermaid()); // Mermaid diagram
Console.WriteLine(workflow.ToDot()); // Graphviz DOT format
JSON Configuration
using WorkflowFramework.Extensions.Configuration;
var loader = new JsonWorkflowDefinitionLoader();
var definition = loader.LoadFromFile("workflow.json");
var stepRegistry = new StepRegistry();
stepRegistry.Register<ValidateOrder>();
stepRegistry.Register<ChargePayment>();
var builder = new WorkflowDefinitionBuilder(stepRegistry);
var workflow = builder.Build(definition);
Testing
using WorkflowFramework.Testing;
// Override specific steps in tests
var harness = new WorkflowTestHarness()
.OverrideStep("ChargePayment", ctx =>
{
ctx.Properties["PaymentCharged"] = true;
return Task.CompletedTask;
});
var result = await harness.ExecuteAsync(workflow, new WorkflowContext());
// Capture events for assertions
var events = new InMemoryWorkflowEvents();
var workflow = Workflow.Create()
.WithEvents(events)
.Step<MyStep>()
.Build();
await workflow.ExecuteAsync(new WorkflowContext());
Assert.Single(events.StepCompleted);
Performance
Run benchmarks with:
dotnet run -c Release --project benchmarks/WorkflowFramework.Benchmarks
Benchmarks cover workflow execution, middleware pipeline overhead, and typed pipeline throughput.
Building
dotnet build WorkflowFramework.slnx
dotnet test WorkflowFramework.slnx
License
MIT © JDH Productions LLC.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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 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 is compatible. 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 is compatible. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (28)
Showing the top 5 NuGet packages that depend on WorkflowFramework:
| Package | Downloads |
|---|---|
|
WorkflowFramework.Extensions.AI
AI and agent integration for WorkflowFramework with LLM, planning, and decision steps. |
|
|
WorkflowFramework.Extensions.DataMapping
Data mapping and transformation engine for WorkflowFramework — field mapping, format conversion, source readers, destination writers, and built-in transformers. |
|
|
WorkflowFramework.Extensions.Connectors.Abstractions
Connector abstractions for WorkflowFramework — base interfaces for source, sink, and bidirectional connectors with configuration models. |
|
|
WorkflowFramework.Extensions.Agents
Core agentic abstractions for WorkflowFramework including tool providers, context sources, hooks, and agent loop steps. |
|
|
WorkflowFramework.Extensions.Integration.Abstractions
Abstraction interfaces for Enterprise Integration Pattern steps in WorkflowFramework. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.15.0 | 1,455 | 2/19/2026 |
| 0.14.1 | 1,329 | 2/19/2026 |
| 0.14.0 | 1,337 | 2/19/2026 |
| 0.13.1 | 1,324 | 2/19/2026 |
| 0.13.0 | 1,313 | 2/19/2026 |
| 0.12.2 | 1,337 | 2/19/2026 |
| 0.12.1 | 1,334 | 2/19/2026 |
| 0.12.0 | 1,333 | 2/19/2026 |
| 0.11.0 | 1,296 | 2/19/2026 |
| 0.10.0 | 1,284 | 2/19/2026 |
| 0.9.0 | 1,278 | 2/19/2026 |
| 0.8.1 | 1,295 | 2/19/2026 |
| 0.8.0 | 1,290 | 2/19/2026 |
| 0.7.0 | 1,235 | 2/19/2026 |
| 0.6.1 | 1,254 | 2/19/2026 |
| 0.6.0 | 1,253 | 2/19/2026 |
| 0.5.4 | 1,208 | 2/19/2026 |
| 0.5.3 | 1,212 | 2/19/2026 |
| 0.5.2 | 1,209 | 2/19/2026 |
| 0.5.1 | 1,209 | 2/19/2026 |