![]() |
VOOZH | about |
dotnet add package WorkflowFramework.Extensions.DataMapping.Schema --version 1.0.3
NuGet\Install-Package WorkflowFramework.Extensions.DataMapping.Schema -Version 1.0.3
<PackageReference Include="WorkflowFramework.Extensions.DataMapping.Schema" Version="1.0.3" />
<PackageVersion Include="WorkflowFramework.Extensions.DataMapping.Schema" Version="1.0.3" />Directory.Packages.props
<PackageReference Include="WorkflowFramework.Extensions.DataMapping.Schema" />Project file
paket add WorkflowFramework.Extensions.DataMapping.Schema --version 1.0.3
#r "nuget: WorkflowFramework.Extensions.DataMapping.Schema, 1.0.3"
#:package WorkflowFramework.Extensions.DataMapping.Schema@1.0.3
#addin nuget:?package=WorkflowFramework.Extensions.DataMapping.Schema&version=1.0.3Install as a Cake Addin
#tool nuget:?package=WorkflowFramework.Extensions.DataMapping.Schema&version=1.0.3Install as a Cake Tool
👁 CI
👁 CodeQL
👁 codecov
👁 NuGet
👁 License: MIT
A fluent, extensible workflow/pipeline engine for .NET with async-first design, middleware, branching, parallel execution, saga/compensation, and rich extensibility.
WorkflowFramework includes a Flowise-style dashboard authoring surface with reusable templates, inline node editing, validation, execution history, and agentic workflow samples.
The dashboard now has a curated Reqnroll + Playwright artifact pipeline:
eng\generate-dashboard-ui-report.ps1 turns scenario manifests into a LivingDoc-style HTML summary plus allure-results\dashboard-summary.json.github\workflows\dashboard-ui-artifacts.yml provides a manual publication path for dashboard UI artifact bundles without slowing default CITo regenerate the current dashboard artifact bundle locally:
dotnet test tests\WorkflowFramework.Dashboard.UITests\WorkflowFramework.Dashboard.UITests.csproj --no-restore --filter "DisplayName~Run local Ollama workflow end-to-end|DisplayName~Cloud providers can be configured without stored credentials|DisplayName~Saved provider keys are not echoed back to the browser|DisplayName~Sample workflows appear in workflow list"
.\eng\generate-dashboard-ui-report.ps1
If/Then/Else for decision-based workflowsParallel()OnStepStarted, OnStepCompleted, OnWorkflowFailed, etc.ForEach, While, DoWhile, Retry for iteration patternsTry/Catch/Finally blocks in your workflowIPipelineStep<TIn, TOut> with chained input/output types[StepName], [StepTimeout], [StepRetry], [StepOrder]IWorkflowValidator / IStepValidator for pre-execution validationIApprovalServiceAuditMiddleware with IAuditStoreWorkflowTestHarness, FakeStep, StepTestBuilderdotnet add package WorkflowFramework
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;
}
}
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
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
Workflow.Create()
.If(ctx => someCondition)
.Then<ProcessStep>()
.Else<RejectStep>()
.Step<FinalStep>()
.Build();
Workflow.Create()
.Parallel(p => p
.Step<SendEmail>()
.Step<SendSms>()
.Step<UpdateDashboard>())
.Build();
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();
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();
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;
}
}
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]
| 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.Extensions.AI |
LLM providers, prompt steps, planning, and routing decisions |
WorkflowFramework.Extensions.Agents |
Agent loops, tool orchestration, context management, and hooks |
WorkflowFramework.Extensions.Agents.Mcp |
MCP transports and MCP-backed tool integration |
WorkflowFramework.Extensions.Agents.Skills |
Skill discovery, loading, and skill-backed tools |
WorkflowFramework.Analyzers |
Roslyn analyzers for common mistakes |
using WorkflowFramework.Extensions.Agents;
using WorkflowFramework.Extensions.AI;
var provider = new EchoAgentProvider();
var registry = new ToolRegistry();
var workflow = Workflow.Create("SupportAgent")
.AgentPlan(provider, options =>
{
options.StepName = "Plan";
options.PromptTemplate = "Plan how to resolve ticket {TicketId}";
options.OutputPropertyName = "Agent.Plan";
})
.AgentLoop(provider, registry, options =>
{
options.SystemPrompt = "You are a support agent.";
options.InitialUserMessageTemplate = "Resolve ticket {TicketId} using the available tools.";
})
.Build();
Prompt-based AI steps render workflow properties such as {TicketId} before sending prompts to the provider, which makes it easier to build task-driven flows without hand-assembling strings in each step.
using WorkflowFramework.Extensions.Polly;
Workflow.Create()
.UseResilience(builder => builder
.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 }))
.Step<UnreliableStep>()
.Build();
using WorkflowFramework.Extensions.Diagnostics;
Workflow.Create()
.Use<TracingMiddleware>() // OpenTelemetry spans
.Use<TimingMiddleware>() // Step timing
.Step<MyStep>()
.Build();
using WorkflowFramework.Extensions.Persistence;
using WorkflowFramework.Extensions.Persistence.InMemory;
var store = new InMemoryWorkflowStateStore();
Workflow.Create()
.Use(new CheckpointMiddleware(store))
.Step<LongRunningStep>()
.Build();
using WorkflowFramework.Extensions.DependencyInjection;
services.AddWorkflowFramework();
services.AddStep<ValidateInput>();
services.AddWorkflowMiddleware<LoggingMiddleware>();
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();
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();
var validation = Workflow.Create("Validation")
.Step<ValidateInput>()
.Step<ValidatePermissions>()
.Build();
var main = Workflow.Create("Main")
.SubWorkflow(validation)
.Step<ProcessData>()
.Build();
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"
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
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
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);
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);
Run benchmarks with:
dotnet run -c Release --project benchmarks/WorkflowFramework.Benchmarks
Benchmarks cover workflow execution, middleware pipeline overhead, and typed pipeline throughput.
dotnet build WorkflowFramework.slnx
dotnet test WorkflowFramework.slnx
© JDH Productions LLC.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 is compatible. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.3 | 45 | 6/16/2026 |
| 1.0.2 | 46 | 6/16/2026 |
| 1.0.1 | 93 | 6/2/2026 |
| 1.0.0 | 101 | 5/23/2026 |
| 0.15.0 | 118 | 2/19/2026 |
| 0.14.1 | 109 | 2/19/2026 |
| 0.14.0 | 108 | 2/19/2026 |
| 0.13.1 | 111 | 2/19/2026 |
| 0.13.0 | 104 | 2/19/2026 |
| 0.12.2 | 120 | 2/19/2026 |
| 0.12.1 | 105 | 2/19/2026 |
| 0.12.0 | 105 | 2/19/2026 |
| 0.11.0 | 110 | 2/19/2026 |
| 0.10.0 | 106 | 2/19/2026 |
| 0.9.0 | 103 | 2/19/2026 |
| 0.8.1 | 104 | 2/19/2026 |
| 0.8.0 | 101 | 2/19/2026 |
| 0.7.0 | 103 | 2/19/2026 |
| 0.6.1 | 109 | 2/19/2026 |
| 0.6.0 | 104 | 2/19/2026 |