WorkflowForge Samples Catalog
Total Samples: 33
Project: src/samples/WorkflowForge.Samples.BasicConsole
Table of Contents
- Learning Path
- Category 1: Basic (Samples 1-4)
- Category 2: Control Flow (Samples 5-8)
- Category 3: Configuration & Middleware (Samples 9-12)
- Category 4: Extensions (Samples 13-18, 21-25)
- Category 5: Advanced (Samples 19-20)
- Category 6: Onboarding & Best Practices (Samples 26-33)
- Key Patterns Across All Samples
- Sample Execution Order (Recommended)
- Sample Coverage Matrix
Learning Path
Beginner → Samples 1-4
Intermediate → Samples 5-12
Advanced → Samples 13-33
Category 1: Basic (Samples 1-4)
Sample 1: HelloWorldSample.cs
Purpose: Simplest possible workflow
Demonstrates:
- Creating a workflow with
WorkflowForge.CreateWorkflow() - Adding inline operations
- Executing with
WorkflowSmith - Basic logging
Key Code Pattern:
var workflow = WorkflowForge.CreateWorkflow("HelloWorld")
.AddOperation("SayHello", (foundry, ct) => {
foundry.Logger.LogInformation("Hello, World!");
return Task.CompletedTask;
})
.Build();
using var smith = WorkflowForge.CreateSmith();
await smith.ForgeAsync(workflow);
Data Flow: None
Complexity: Trivial
Sample 2: DataPassingSample.cs
Purpose: Dictionary-based data flow between operations
Demonstrates:
- Using
foundry.SetProperty/GetPropertyOrDefault(typed helpers) - Passing data between operations
- Reading and writing properties
Key Code Pattern:
foundry.SetProperty("Input", inputData);
// Operation 1
foundry.SetProperty("ProcessedData", result);
// Operation 2
var data = foundry.GetPropertyOrDefault<DataType>("ProcessedData");
Data Flow: Dictionary-based (PRIMARY pattern)
Complexity: Simple
Sample 3: MultipleOutcomesSample.cs
Purpose: Operations with different outcomes
Demonstrates:
- Conditional logic within operations
- Different execution paths
- Property-based branching
Key Code Pattern:
var approved = foundry.GetPropertyOrDefault<bool>("IsApproved");
if (approved) {
// Happy path
} else {
// Alternative path
}
Data Flow: Dictionary-based
Complexity: Simple
Sample 4: ClassBasedOperationsSample.cs
Purpose: Demonstrate class-based operations as the preferred pattern
Demonstrates:
- Class-based operations implementing
IWorkflowOperation - Output chaining between operations
- Property-based state management
- Production-ready operation structure
Key Code Pattern:
public sealed class ValidateOrderOperation : IWorkflowOperation
{
public Guid Id { get; } = Guid.NewGuid();
public string Name => "ValidateOrder";
public bool SupportsRestore => false;
public Task<object?> ForgeAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
{
var userName = foundry.Properties["user_name"]?.ToString();
var orderTotal = (decimal)foundry.Properties["order_total"]!;
foundry.SetProperty("order_valid", orderTotal > 0 && !string.IsNullOrEmpty(userName));
return Task.FromResult(inputData);
}
public Task RestoreAsync(object? outputData, IWorkflowFoundry foundry, CancellationToken ct)
=> Task.CompletedTask;
public void Dispose() { }
}
// Usage
foundry
.WithOperation(new ValidateOrderOperation())
.WithOperation(new CalculateShippingOperation())
.WithOperation(new ProcessPaymentOperation());
Data Flow: Dictionary-based via foundry.Properties
Complexity: Simple
Recommendation: Use class-based operations for production scenarios
Category 2: Control Flow (Samples 5-8)
Sample 5: ConditionalWorkflowSample.cs
Purpose: Conditional branching in workflows
Demonstrates:
ConditionalWorkflowOperationusage- Predicate-based branching
- True/false operation execution
Key Code Pattern:
var conditional = new ConditionalWorkflowOperation(
name: "CheckCondition",
predicate: foundry => foundry.GetPropertyOrDefault<bool>("Condition"),
onTrue: new TrueOperation(),
onFalse: new FalseOperation()
);
Data Flow: Dictionary-based
Complexity: Intermediate
Sample 6: ForEachLoopSample.cs
Purpose: Collection iteration
Demonstrates:
ForEachWorkflowOperationusage- Sequential vs. parallel execution
- Shared vs. independent data strategies
- Result aggregation
Key Code Pattern:
// Shared input - all operations receive the same input data
var sharedInput = ForEachWorkflowOperation.CreateSharedInput(
new[] { op1, op2, op3 },
maxConcurrency: 4,
name: "ProcessAllItems"
);
// Split input - input collection is distributed among operations
var splitInput = ForEachWorkflowOperation.CreateSplitInput(
new[] { op1, op2, op3 },
maxConcurrency: 2
);
// No input - operations receive null input
var noInput = ForEachWorkflowOperation.CreateNoInput(operations);
Data Flow: Dictionary-based + collection iteration
Complexity: Intermediate
Sample 7: ErrorHandlingSample.cs
Purpose: Exception handling and compensation
Demonstrates:
- Try-catch in operations
RestoreAsync()compensation- Saga pattern
- Error event handling
Key Code Pattern:
protected override async Task<object?> ForgeAsyncCore(...) {
try {
// Operation logic
} catch (Exception ex) {
// Handle error
throw;
}
}
public override async Task RestoreAsync(...) {
// Compensate/rollback
}
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Saga (compensation)
Sample 8: BuiltInOperationsSample.cs
Purpose: Showcase all built-in operations
Demonstrates:
LoggingOperationDelayOperationConditionalWorkflowOperationForEachWorkflowOperationActionWorkflowOperationDelegateWorkflowOperation
Key Code Pattern:
.AddOperation(new LoggingOperation("Message", WorkflowForgeLogLevel.Information))
.AddOperation(new DelayOperation(TimeSpan.FromSeconds(1)))
.AddOperation(new ConditionalWorkflowOperation(...))
.AddOperation(ForEachWorkflowOperation.CreateSharedInput(operations))
Data Flow: Dictionary-based
Complexity: Intermediate
Category 3: Configuration & Middleware (Samples 9-12)
Sample 9: OptionsPatternSample.cs
Purpose: Configuration management
Demonstrates:
WorkflowForgeOptionsconfiguration- Options pattern
- Configuration binding
- appsettings.json integration
Key Code Pattern:
services.Configure<WorkflowForgeOptions>(
Configuration.GetSection(WorkflowForgeOptions.DefaultSectionName)
);
var config = serviceProvider.GetRequiredService<IOptions<WorkflowForgeOptions>>();
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Options pattern
Sample 10: ConfigurationProfilesSample.cs
Purpose: Environment-specific configuration
Demonstrates:
WorkflowForgeOptionspresets- Production vs. high-throughput tradeoffs
Key Code Pattern:
var productionOptions = new WorkflowForgeOptions
{
Enabled = true,
ContinueOnError = false,
FailFastCompensation = false,
ThrowOnCompensationError = true,
EnableOutputChaining = true
};
var highThroughputOptions = new WorkflowForgeOptions
{
Enabled = true,
ContinueOnError = true,
FailFastCompensation = false,
ThrowOnCompensationError = false,
EnableOutputChaining = true
};
var productionFoundry = WorkflowForge.CreateFoundry("Workflow", options: productionOptions);
var highPerfFoundry = WorkflowForge.CreateFoundry("Workflow", options: highThroughputOptions);
Data Flow: Dictionary-based
Complexity: Intermediate
Use Case: Environment-specific deployments
Sample 11: WorkflowEventsSample.cs
Purpose: Event system usage
Demonstrates:
IWorkflowLifecycleEvents(workflow events)IOperationLifecycleEvents(operation events)ICompensationLifecycleEvents(compensation events)- Event subscription and unsubscription
- Strongly-typed event args
Key Code Pattern:
smith.WorkflowStarted += (sender, args) => { ... };
smith.WorkflowCompleted += (sender, args) => { ... };
smith.WorkflowFailed += (sender, args) => { ... };
foundry.OperationStarted += (sender, args) => { ... };
foundry.OperationCompleted += (sender, args) => { ... };
foundry.OperationFailed += (sender, args) => { ... };
smith.CompensationTriggered += (sender, args) => { ... };
smith.CompensationCompleted += (sender, args) => { ... };
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Event-driven architecture
Sample 12: MiddlewareSample.cs
Purpose: Custom middleware creation
Demonstrates:
IWorkflowOperationMiddlewareinterface- Russian Doll execution pattern
- Middleware ordering
- Cross-cutting concerns (timing, logging, validation)
Key Code Pattern:
public class TimingMiddleware : IWorkflowOperationMiddleware
{
public async Task<object?> ExecuteAsync(
IWorkflowOperation operation,
IWorkflowFoundry foundry,
object? inputData,
Func<CancellationToken, Task<object?>> next,
CancellationToken cancellationToken)
{
var sw = Stopwatch.StartNew();
try {
return await next(cancellationToken);
} finally {
sw.Stop();
foundry.SetProperty($"{operation.Name}.ExecutionTime", sw.ElapsedMilliseconds);
}
}
}
foundry.AddMiddleware(new TimingMiddleware());
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Middleware/Pipeline
Category 4: Extensions (Samples 13-18, 21-25)
Sample 13: SerilogIntegrationSample.cs
Purpose: Structured logging with Serilog
Demonstrates:
- Serilog extension usage
- Context enrichment
- Structured log output
- Multiple sinks (Console, File)
Key Code Pattern:
using WorkflowForge.Extensions.Logging.Serilog;
var logger = SerilogLoggerFactory.CreateLogger(new SerilogLoggerOptions
{
MinimumLevel = "Information",
EnableConsoleSink = true
});
var smith = WorkflowForge.CreateSmith(logger);
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Logging.Serilog
Dependency Isolation: ILRepack internalized (Serilog only)
Sample 14: PollyResilienceSample.cs
Purpose: Resilience patterns (retry, circuit breaker)
Demonstrates:
- Polly extension usage
- Retry policies
- Circuit breaker policies
- Timeout policies
- Fallback handlers
Key Code Pattern:
// Comprehensive policy with retry, circuit breaker, and timeout
foundry.UsePollyComprehensive(
maxRetryAttempts: 3,
circuitBreakerThreshold: 5,
circuitBreakerDuration: TimeSpan.FromSeconds(30),
timeoutDuration: TimeSpan.FromSeconds(10));
// Or use individual policies
foundry.UsePollyRetry(maxRetryAttempts: 3);
foundry.UsePollyCircuitBreaker(failureThreshold: 5, durationOfBreak: TimeSpan.FromSeconds(30));
foundry.UsePollyTimeout(TimeSpan.FromSeconds(10));
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Resilience.Polly
Dependency Isolation: ILRepack internalized (Polly only)
Sample 15: OpenTelemetryObservabilitySample.cs
Purpose: Distributed tracing and metrics
Demonstrates:
- OpenTelemetry extension usage
- Span creation and enrichment
- Distributed tracing
- Metrics collection
Key Code Pattern:
services.AddOpenTelemetryTracing(builder => builder
.AddWorkflowForgeInstrumentation()
.AddJaegerExporter()
);
foundry.EnableOpenTelemetry();
Data Flow: Dictionary-based
Complexity: Advanced
Extension: WorkflowForge.Extensions.Observability.OpenTelemetry
Dependency Isolation: ILRepack internalized (OpenTelemetry only)
Sample 16: HealthChecksSample.cs
Purpose: Health monitoring
Demonstrates:
- Health checks extension usage
- Custom health checks
- ASP.NET Core integration
- Health check endpoints
Key Code Pattern:
services.AddHealthChecks()
.AddWorkflowForgeHealthCheck();
app.MapHealthChecks("/health");
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Observability.HealthChecks
Dependency Isolation: External dependencies (Microsoft/System runtime unification)
Sample 17: PerformanceMonitoringSample.cs
Purpose: Performance metrics collection
Demonstrates:
- Performance monitoring extension
- Operation timing
- Memory allocation tracking
- Throughput metrics
Key Code Pattern:
foundry.EnablePerformanceMonitoring();
var metrics = foundry.GetPerformanceMetrics();
Console.WriteLine($"Total Duration: {metrics.TotalDuration}ms");
Console.WriteLine($"Memory Allocated: {metrics.MemoryAllocated}KB");
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Observability.Performance
Dependency Isolation: None (pure WorkflowForge)
Sample 18: PersistenceSample.cs
Purpose: Workflow state persistence
Demonstrates:
- Persistence extension usage
- State checkpointing
- Custom persistence providers
- State snapshots
Key Code Pattern:
var persistenceProvider = new FilePersistenceProvider("./state");
foundry.UsePersistence(persistenceProvider);
// Workflow state automatically checkpointed
Data Flow: Dictionary-based
Complexity: Advanced
Extension: WorkflowForge.Extensions.Persistence
Dependency Isolation: None (pure WorkflowForge)
Sample 21: RecoveryOnlySample.cs
Purpose: Workflow recovery and resume
Demonstrates:
- Recovery extension usage
- Resume from checkpoint
- Replay failed operations
- State reconstruction
Key Code Pattern:
var recoveryProvider = new FileRecoveryProvider("./recovery");
foundry.UseRecovery(recoveryProvider);
// Resume workflow from checkpoint
await smith.ResumeAsync(workflowId);
Data Flow: Dictionary-based
Complexity: Advanced
Extension: WorkflowForge.Extensions.Persistence.Recovery
Dependency Isolation: None (pure WorkflowForge)
Sample 22: ResilienceRecoverySample.cs
Purpose: Combined resilience + recovery
Demonstrates:
- Using multiple extensions together
- Resilience policies with recovery
- Complex failure scenarios
- Multi-layer error handling
Key Code Pattern:
// Add resilience middleware to foundry
foundry.UsePollyRetry(maxRetryAttempts: 3);
// Execute with recovery support
await smith.ForgeWithRecoveryAsync(
workflow, foundry, persistenceProvider,
foundryKey, workflowKey);
// Workflow benefits from both retry and recovery
Data Flow: Dictionary-based
Complexity: Advanced
Extensions: Resilience.Polly + Persistence.Recovery
Pattern: Defense in depth
Sample 23: ValidationSample.cs
Purpose: DataAnnotations validation
Demonstrates:
- Validation extension usage
- DataAnnotations validation
- Middleware-based validation
- Manual validation
- Custom validators
Key Code Pattern:
using System.ComponentModel.DataAnnotations;
public class Order
{
[Required]
public string CustomerId { get; set; } = string.Empty;
[Range(0.01, double.MaxValue)]
public decimal Amount { get; set; }
}
foundry.UseValidation(
f => f.GetPropertyOrDefault<Order>("Order"));
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Validation
Dependency Isolation: No third-party dependencies
Sample 24: AuditSample.cs
Purpose: Comprehensive audit logging
Demonstrates:
- Audit extension usage
- Pluggable audit providers
- In-memory provider
- Custom audit entries
- Compliance logging
Key Code Pattern:
var auditProvider = new InMemoryAuditProvider();
foundry.EnableAudit(
auditProvider,
initiatedBy: "user@example.com",
includeMetadata: true
);
// All operations automatically audited
// Access audit entries
var entries = auditProvider.Entries;
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.Audit
Dependency Isolation: None (pure WorkflowForge)
Sample 25: ConfigurationSample.cs
Purpose: Configuration-driven workflow setup using appsettings.json
Demonstrates:
- Loading configuration from appsettings.json
- Enabling/disabling extensions via configuration
- Using IOptions pattern for type-safe configuration
- Configuration validation on startup
Key Code Pattern:
// Setup DI with configuration
var services = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
// Register extension configurations
services.AddAuditConfiguration(configuration);
services.AddValidationConfiguration(configuration);
services.AddPersistenceConfiguration(configuration);
services.AddRecoveryConfiguration(configuration);
services.AddWorkflowForgePolly(configuration);
// Check if extension is enabled before use
var auditOptions = serviceProvider.GetRequiredService<IOptions<AuditOptions>>();
if (auditOptions.Value.Enabled)
{
foundry.EnableAudit(auditProvider);
}
Data Flow: Dictionary-based
Complexity: Advanced
Extensions: All (configuration-driven)
Pattern: Options pattern + feature toggles
Category 5: Advanced (Samples 19-20)
Sample 19: ComprehensiveIntegrationSample.cs
Purpose: All features combined
Demonstrates:
- Multiple extensions together
- Complex workflow scenarios
- Real-world patterns
- Production best practices
Key Code Pattern:
foundry.EnablePerformanceMonitoring();
foundry.UsePollyRetry(maxRetryAttempts: 3);
foundry.UseValidation<OrderDto>(f => f.GetPropertyOrDefault<OrderDto>("Order"));
foundry.EnableAudit(auditProvider);
foundry.UsePersistence(persistenceProvider);
foundry.AddMiddleware(new TimingMiddleware());
// Complex workflow with all features enabled
Data Flow: Dictionary-based
Complexity: Advanced
Extensions: All combined
Use Case: Production-grade workflows
Sample 20: OperationCreationPatternsSample.cs
Purpose: Different ways to create operations
Demonstrates:
- Class-based operations (recommended)
- Inline async operations
- Inline sync operations
- Delegate operations
- Action operations
- Generic typed operations
- When to use each pattern
Key Code Pattern:
// Class-based (production-recommended)
public class ProcessOrderOperation : WorkflowOperationBase
{
protected override async Task<object?> ForgeAsyncCore(...) { ... }
}
// Inline async
.AddOperation("Process", async (foundry, ct) => { ... })
// Inline sync
.AddOperation("Process", (foundry) => { ... })
// Typed generic
public class ProcessOperation : IWorkflowOperation<Order, OrderResult>
{
public async Task<OrderResult> ForgeAsync(Order input, ...) { ... }
}
Data Flow: Dictionary-based + type-safe
Complexity: Advanced
Recommendation: Class-based operations for production scenarios
Category 6: Onboarding & Best Practices (Samples 26-33)
Sample 26: DependencyInjectionSample.cs
Purpose: Configure WorkflowForge with dependency injection
Demonstrates:
- Configuring WorkflowForge via DI container
- Registering
IWorkflowSmithin services - Resolving workflows from DI
- Using
AddWorkflowForge()andAddWorkflowSmith()
Key Code Pattern:
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(configuration);
services.AddSingleton<IWorkflowForgeLogger>(_ => new ConsoleLogger("WF-DI"));
services.AddWorkflowForge(configuration);
services.AddWorkflowSmith();
services.AddSingleton<IOrderIdGenerator, OrderIdGenerator>();
using var provider = services.BuildServiceProvider();
var smith = provider.GetRequiredService<IWorkflowSmith>();
var workflow = WorkflowForge.CreateWorkflow("DiConfiguredWorkflow")
.AddOperation(new GenerateOrderIdOperation())
.Build();
await smith.ForgeAsync(workflow);
Data Flow: Dictionary-based
Complexity: Intermediate
Extension: WorkflowForge.Extensions.DependencyInjection
Pattern: Dependency Injection + Options pattern
Sample 27: WorkflowMiddlewareSample.cs
Purpose: Workflow-level middleware vs operation middleware
Demonstrates:
IWorkflowMiddlewareinterface- Adding workflow middleware to smith
- Timing and audit at workflow level
- Difference between workflow and operation middleware
Key Code Pattern:
public sealed class WorkflowTimingMiddleware : IWorkflowMiddleware
{
public async Task ExecuteAsync(
IWorkflow workflow,
IWorkflowFoundry foundry,
Func<Task> next,
CancellationToken cancellationToken)
{
var start = DateTimeOffset.UtcNow;
foundry.Logger.LogInformation($"[WorkflowTiming] Starting {workflow.Name}");
await next().ConfigureAwait(false);
var duration = DateTimeOffset.UtcNow - start;
foundry.Logger.LogInformation($"[WorkflowTiming] Completed in {duration.TotalMilliseconds:F0}ms");
}
}
var smith = WorkflowForge.CreateSmith(logger);
smith.AddWorkflowMiddleware(new WorkflowTimingMiddleware());
smith.AddWorkflowMiddleware(new WorkflowAuditMiddleware());
await smith.ForgeAsync(workflow);
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Workflow-level middleware pipeline
Sample 28: CancellationAndTimeoutSample.cs
Purpose: Cancellation tokens and operation timeouts
Demonstrates:
- Using
CancellationTokenin operations OperationTimeoutMiddlewareusage- Handling
TimeoutExceptionandOperationCanceledException - Graceful shutdown patterns
Key Code Pattern:
// Timeout via middleware
using var foundry = WorkflowForge.CreateFoundry("TimeoutDemo");
foundry.AddMiddleware(new OperationTimeoutMiddleware(TimeSpan.FromMilliseconds(100), foundry.Logger));
foundry.WithOperation(new SlowOperation("SlowOp", 300));
try
{
await foundry.ForgeAsync();
}
catch (TimeoutException ex)
{
Console.WriteLine($"Timeout triggered: {ex.Message}");
}
// Cancellation via token
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
try
{
await foundry.ForgeAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation cancelled.");
}
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Timeout + cancellation handling
Sample 29: ContinueOnErrorSample.cs
Purpose: ContinueOnError behavior and aggregate exception handling
Demonstrates:
WorkflowForgeOptions.ContinueOnError = true- Workflow continues after operation failure
AggregateExceptionat end of workflow- Use case for batch processing
Key Code Pattern:
var options = new WorkflowForgeOptions { ContinueOnError = true };
using var foundry = WorkflowForge.CreateFoundry("ContinueOnErrorDemo", options: options);
foundry
.WithOperation(new SuccessOperation("First"))
.WithOperation(new FailingOperation("FailurePoint"))
.WithOperation(new SuccessOperation("Final"));
try
{
await foundry.ForgeAsync();
}
catch (AggregateException ex)
{
Console.WriteLine($"AggregateException with {ex.InnerExceptions.Count} error(s).");
}
// Final operation still executed despite earlier failure
Console.WriteLine($"Final operation ran: {foundry.GetPropertyOrDefault("final.ran", false)}");
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Error aggregation
Sample 30: CompensationBehaviorSample.cs
Purpose: Compensation behaviors and error handling strategies
Demonstrates:
FailFastCompensationoptionThrowOnCompensationErroroption- Compensation success vs failure scenarios
RestoreAsync()implementation
Key Code Pattern:
var options = new WorkflowForgeOptions
{
FailFastCompensation = true,
ThrowOnCompensationError = true
};
using var foundry = WorkflowForge.CreateFoundry("CompensationDemo", options: options);
var workflow = WorkflowForge.CreateWorkflow("CompensationWorkflow")
.AddOperation(new CompensatableOperation("StepA"))
.AddOperation(new CompensatableOperation("StepB"))
.AddOperation(new FailingOperation("FailurePoint"))
.Build();
try
{
await smith.ForgeAsync(workflow, foundry);
}
catch (AggregateException ex)
{
Console.WriteLine($"Compensation errors: {ex.InnerExceptions.Count}");
}
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Saga compensation
Sample 31: FoundryReuseSample.cs
Purpose: Reusing a foundry across multiple workflows
Demonstrates:
- Creating foundry via
smith.CreateFoundry() - Running multiple workflows with same foundry
- Property persistence across executions
- Foundry lifecycle management
Key Code Pattern:
var smith = WorkflowForge.CreateSmith();
using var foundry = smith.CreateFoundry();
var workflowA = WorkflowForge.CreateWorkflow("ReuseA")
.AddOperation(new RecordRunOperation("FirstWorkflow"))
.Build();
var workflowB = WorkflowForge.CreateWorkflow("ReuseB")
.AddOperation(new RecordRunOperation("SecondWorkflow"))
.Build();
await smith.ForgeAsync(workflowA, foundry);
await smith.ForgeAsync(workflowB, foundry);
// Properties persist across workflow executions
var runs = foundry.GetPropertyOrDefault<List<string>>("runs") ?? new();
Console.WriteLine($"Runs: {string.Join(", ", runs)}"); // Output: FirstWorkflow, SecondWorkflow
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Foundry reuse
Sample 32: OutputChainingSample.cs
Purpose: Operation output becoming the next operation’s input
Demonstrates:
- Return values from
ForgeAsync()flow to next operation EnableOutputChainingoption behavior- Type-safe data flow between operations
- Chained transformations
Key Code Pattern:
using var foundry = WorkflowForge.CreateFoundry("OutputChainingDemo");
foundry
.WithOperation(new SeedNumberOperation()) // Returns: 7
.WithOperation(new MultiplyOperation(3)) // Receives: 7, Returns: 21
.WithOperation(new FormatResultOperation()); // Receives: 21, Returns: "Result: 21"
await foundry.ForgeAsync();
// Each operation receives the previous operation's output as inputData
public Task<object?> ForgeAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
{
var value = inputData is int number ? number : 0;
var result = value * _multiplier;
Console.WriteLine($"Multiplied {value} by {_multiplier} = {result}");
return Task.FromResult<object?>(result);
}
Data Flow: Output chaining + Dictionary-based
Complexity: Intermediate
Pattern: Pipeline transformation
Sample 33: ServiceProviderResolutionSample.cs
Purpose: Resolving services inside operations via foundry.ServiceProvider
Demonstrates:
- Accessing
foundry.ServiceProviderin operations - Resolving registered services at runtime
- DI-aware operation design
- Service abstraction patterns
Key Code Pattern:
var services = new ServiceCollection();
services.AddSingleton<IWorkflowForgeLogger>(_ => new ConsoleLogger("WF-Services"));
services.AddSingleton<IPriceCalculator, PriceCalculator>();
using var provider = services.BuildServiceProvider();
var smith = WorkflowForge.CreateSmith(
provider.GetRequiredService<IWorkflowForgeLogger>(),
provider);
var workflow = WorkflowForge.CreateWorkflow("ServiceProviderDemo")
.AddOperation(new CalculateTotalOperation())
.Build();
await smith.ForgeAsync(workflow);
// Inside the operation
public Task<object?> ForgeAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
{
var calculator = foundry.ServiceProvider?.GetRequiredService<IPriceCalculator>()
?? throw new InvalidOperationException("IPriceCalculator not registered.");
var total = calculator.CalculateTotal(subtotal: 120m, taxRate: 0.08m);
return Task.FromResult<object?>(total);
}
Data Flow: Dictionary-based
Complexity: Intermediate
Pattern: Service Locator via DI container
Key Patterns Across All Samples
- Data Flow: Dictionary-based via
foundry.Properties(33/33 samples) - Type Safety: Generic
IWorkflowOperation<TInput, TOutput>mentioned but rarely used - Production Focus: Class-based operations recommended over inline
- Extension Usage: 13/33 samples demonstrate extensions
- Error Handling: Explicit compensation in 2 samples, implicit in all via framework
- Configuration: 3 samples dedicated to configuration patterns
- Events: 1 sample dedicated to event system, events used in others
- Middleware: 1 sample dedicated, middleware used in many extensions
Sample Execution Order (Recommended)
For New Users (Learning Path):
- HelloWorldSample (1)
- DataPassingSample (2)
- ClassBasedOperationsSample (4)
- MultipleOutcomesSample (3)
- ConditionalWorkflowSample (5)
- ForEachLoopSample (6)
- BuiltInOperationsSample (8)
- ErrorHandlingSample (7)
- OptionsPatternSample (9)
- ConfigurationProfilesSample (10)
- WorkflowEventsSample (11)
- MiddlewareSample (12)
- DependencyInjectionSample (26)
- OutputChainingSample (32)
- ServiceProviderResolutionSample (33)
- SerilogIntegrationSample (13)
- PerformanceMonitoringSample (17)
- ValidationSample (23)
- AuditSample (24)
- ConfigurationSample (25)
- PollyResilienceSample (14)
- CancellationAndTimeoutSample (28)
- ContinueOnErrorSample (29)
- CompensationBehaviorSample (30)
- FoundryReuseSample (31)
- WorkflowMiddlewareSample (27)
- PersistenceSample (18)
- RecoveryOnlySample (21)
- ResilienceRecoverySample (22)
- HealthChecksSample (16)
- OpenTelemetryObservabilitySample (15)
- OperationCreationPatternsSample (20)
- ComprehensiveIntegrationSample (19)
Sample Coverage Matrix
| Feature | Sample(s) |
|---|---|
| Basic workflow creation | 1 |
| Data passing (dictionary) | 2, 3, ALL |
| Class-based operations | 4, 20, ALL |
| Operation patterns | 20 |
| Conditional branching | 3, 5, 8 |
| ForEach loops | 6, 8 |
| Error handling | 7, 29, 30 |
| Built-in operations | 8 |
| Configuration | 9, 10, 25 |
| Events | 11 |
| Middleware (operation) | 12 |
| Middleware (workflow) | 27 |
| Logging (Serilog) | 13 |
| Resilience (Polly) | 14, 22 |
| OpenTelemetry | 15 |
| Health checks | 16 |
| Performance monitoring | 17 |
| Persistence | 18, 22 |
| Recovery | 21, 22 |
| Validation | 23 |
| Audit | 24 |
| Configuration-driven extensions | 25 |
| Dependency injection | 26 |
| Cancellation + timeout | 28 |
| ContinueOnError | 29 |
| Compensation behaviors | 30 |
| Foundry reuse | 31 |
| Output chaining | 32 |
| Service provider resolution | 33 |
| Comprehensive integration | 19 |
Related Documentation
- Getting Started - Learn the basics before exploring samples
- Operations Guide - Detailed operation documentation
- Extensions - Extension usage in samples
- Configuration - Configuration patterns in samples
- Events System - Event handling in samples