Workflow Engine
Multi-step business processes with actions, rules, triggers, retry policies, conditional branching, and parallel execution
Source files
DataManagementModelsStandard/Workflow/WorkFlow.csDataManagementModelsStandard/Workflow/WorkFlowStep.csDataManagementModelsStandard/Workflow/WorkFlowAction.csDataManagementModelsStandard/Workflow/WorkFlowTrigger.csDataManagementModelsStandard/Workflow/WorkFlowRetryPolicy.csDataManagementEngineStandard/Editor/WorkFlowEditor.cs
Table of Contents
Overview
The Workflow Engine provides a flexible system for defining and executing multi-step business processes. Workflows consist of steps connected in a directed graph, with each step containing actions, rules, and transitions.
The engine supports:
- Directed graphs — Steps with previous/next references (not just linear)
- Conditional branching — Route execution based on rule evaluation
- Parallel execution — Multiple branches executing simultaneously
- Parameter passing — Data flow between steps via InParameters/OutParameters
- Error handling — Step-level retry policies with exponential backoff
- Triggers — Manual, scheduled, event-driven, webhook, pipeline completion
WorkFlow Class
public class WorkFlow : IWorkFlow
{
public List<IWorkFlowStep> Datasteps { get; set; }
public List<string> DataSources { get; set; }
public string WorkSpaceDatabase { get; set; }
public string WorkSpaceFolder { get; set; }
public string DataWorkFlowName { get; set; }
public string Description { get; set; }
public int ID { get; set; }
public string GuidID { get; set; }
public List<WorkFlow_Run_Result> workFlow_Run_Results { get; set; }
public event EventHandler<WorkFlowEventArgs> WorkFlowStarted;
public event EventHandler<WorkFlowEventArgs> WorkFlowEnded;
public event EventHandler<WorkFlowEventArgs> WorkFlowRunning;
}
Properties
| Property | Type | Description |
|---|---|---|
Datasteps | List<IWorkFlowStep> | Step nodes in the workflow graph |
DataSources | List<string> | Datasource names used by steps |
WorkSpaceDatabase | string | Workspace database for intermediate storage |
DataWorkFlowName | string | Unique workflow identifier |
workFlow_Run_Results | List<WorkFlow_Run_Result> | Execution history |
WorkFlowStep
public class WorkFlowStep : IWorkFlowStep
{
public int Seq { get; set; }
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string StepType { get; set; }
public string Code { get; set; }
public bool IsFinish { get; set; }
public bool IsRunning { get; set; }
// Graph navigation
public IWorkFlowStep PrevStep { get; set; }
public List<IWorkFlowStep> NextStep { get; set; }
// Actions to execute in this step
public List<IWorkFlowAction> Actions { get; set; }
// Parameter flow
public List<IPassedArgs> InParameters { get; set; }
public List<IPassedArgs> OutParameters { get; set; }
// Rules for conditional execution
public List<IWorkFlowRule> Rules { get; set; }
public event EventHandler<WorkFlowEventArgs> WorkFlowStepStarted;
public event EventHandler<WorkFlowEventArgs> WorkFlowStepEnded;
public event EventHandler<WorkFlowEventArgs> WorkFlowStepRunning;
}
Step Properties
| Property | Description |
|---|---|
Seq | Sequence number for ordering |
IsFinish | Terminal step marker (end of workflow) |
PrevStep | Single predecessor in graph |
NextStep | Multiple successors (forking) |
Actions | Actions executed when step runs |
InParameters | Input data from previous step |
OutParameters | Output data for next step |
Rules | Conditional execution rules |
WorkFlowAction
public class WorkFlowAction : IWorkFlowAction
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ActionTypeName { get; set; }
public string Code { get; set; }
public string ClassName { get; set; }
public IDMEEditor DMEEditor { get; set; }
// Action chain
public IWorkFlowAction PrevAction { get; set; }
public List<IWorkFlowAction> NextAction { get; set; }
// Parameter flow
public List<IPassedArgs> InParameters { get; set; }
public List<IPassedArgs> OutParameters { get; set; }
// Conditional rules
public List<IWorkFlowRule> Rules { get; set; }
public bool IsFinish { get; set; }
public bool IsRunning { get; set; }
// Execution
public virtual PassedArgs PerformAction(
IProgress<PassedArgs> progress, CancellationToken token);
public virtual PassedArgs PerformAction(
IProgress<PassedArgs> progress, CancellationToken token,
Func<PassedArgs, object> actionToExecute);
public virtual PassedArgs StopAction();
}
WorkFlowTrigger
public enum TriggerKind
{
Manual,
Scheduled,
EventDriven,
WebHook,
PipelineCompletion
}
public class WorkFlowTrigger
{
public TriggerKind Kind { get; set; } = TriggerKind.Manual;
public string? CronExpression { get; set; }
public string? EventTopic { get; set; }
public string? PipelineId { get; set; }
}
Trigger Kinds
| Kind | Config Fields | Example |
|---|---|---|
Manual | None | User clicks "Execute" |
Scheduled | CronExpression | "0 0 * * *" (daily midnight) |
EventDriven | EventTopic | "orders.created" |
WebHook | EventTopic (URL) | "/api/hooks/slack" |
PipelineCompletion | PipelineId | "daily-sync" |
WorkFlowRetryPolicy
public class WorkFlowRetryPolicy
{
public static WorkFlowRetryPolicy None { get; } = new() { MaxRetries = 0 };
public static WorkFlowRetryPolicy Once { get; } = new() { MaxRetries = 1, DelaySeconds = 5 };
public int MaxRetries { get; set; } = 0;
public double DelaySeconds { get; set; } = 5;
public bool ExponentialBackoff { get; set; } = false;
public double MaxDelaySeconds { get; set; } = 300;
public TimeSpan GetDelay(int attempt)
{
if (!ExponentialBackoff)
return TimeSpan.FromSeconds(DelaySeconds);
var delay = DelaySeconds * Math.Pow(2, attempt);
return TimeSpan.FromSeconds(Math.Min(delay, MaxDelaySeconds));
}
}
Backoff Examples
| Attempt | Fixed (5s) | Exponential (2s base) |
|---|---|---|
| 1 | 5s | 2s |
| 2 | 5s | 4s |
| 3 | 5s | 8s |
| 4 | 5s | 16s |
| 5 | 5s | 32s |
Workflow Rules
Rules enable conditional execution, branching, and validation within workflow steps and actions:
public interface IWorkFlowRule
{
string RuleName { get; set; }
bool Evaluate(IPassedArgs context);
}
// Conditional execution based on step outcome
step.Rules.Add(new WorkFlowRule
{
RuleName = "CheckIfApproved",
Evaluate = context => context.ParameterInt1 == 100
});
WorkFlowEditor
public class WorkFlowEditor
{
public WorkFlowEditor(IDMEEditor editor);
// Execution
public Task<WorkFlowResult> ExecuteAsync(
WorkFlow workflow,
WorkFlowContext context);
// Design-time
public void AddStep(WorkFlow workflow, WorkFlowStep step);
public void LinkSteps(WorkFlowStep from, WorkFlowStep to);
public bool ValidateWorkflow(WorkFlow workflow, out List<string> errors);
}
WorkFlowResult
| Property | Description |
|---|---|
Success | All steps completed successfully |
FailedStep | Name of the failing step (if any) |
Duration | Total execution time |
Error | Exception details if failed |
StepResults | Per-step outcome details |
Conditional Branching
// Setup branching workflow
var validateStep = new WorkFlowStep { Name = "ValidateOrder", Seq = 1 };
var approveStep = new WorkFlowStep { Name = "ApproveOrder", Seq = 2 };
var rejectStep = new WorkFlowStep { Name = "RejectOrder", Seq = 2 };
// Branch based on validation result
validateStep.NextStep = new List<IWorkFlowStep> { approveStep, rejectStep };
approveStep.Rules.Add(new WorkFlowRule
{
RuleName = "OrderApproved",
Evaluate = ctx => ctx.ParameterInt1 == 100
});
rejectStep.Rules.Add(new WorkFlowRule
{
RuleName = "OrderRejected",
Evaluate = ctx => ctx.ParameterInt1 != 100
});
// Only execute branch whose rules evaluate to true
// Rules provide flexible branching without hard-coded logic
Parallel Execution
var mainStep = new WorkFlowStep { Name = "ProcessOrder", Seq = 1 };
// Multiple parallel branches
var paymentStep = new WorkFlowStep { Name = "ProcessPayment", Seq = 2 };
var inventoryStep = new WorkFlowStep { Name = "UpdateInventory", Seq = 2 };
var notifyStep = new WorkFlowStep { Name = "SendNotification", Seq = 2 };
// Fork: one predecessor, multiple successors
mainStep.NextStep = new List<IWorkFlowStep> { paymentStep, inventoryStep, notifyStep };
// Each runs independently (parallel)
paymentStep.PrevStep = mainStep;
inventoryStep.PrevStep = mainStep;
notifyStep.PrevStep = mainStep;
// Join back to single step
var completeStep = new WorkFlowStep { Name = "CompleteOrder", Seq = 3, IsFinish = true };
paymentStep.NextStep.Add(completeStep);
inventoryStep.NextStep.Add(completeStep);
notifyStep.NextStep.Add(completeStep);
Usage Examples
Order Processing Workflow
var workflow = new WorkFlow
{
DataWorkFlowName = "OrderProcessing",
Description = "End-to-end order processing",
Datasteps = new List<IWorkFlowStep>
{
new WorkFlowStep
{
Name = "ValidateOrder", Seq = 1,
Actions = { new ValidateOrderAction() },
NextStep = new List<IWorkFlowStep>()
},
new WorkFlowStep
{
Name = "ProcessPayment", Seq = 2,
Actions = { new ProcessPaymentAction() },
NextStep = new List<IWorkFlowStep>()
},
new WorkFlowStep
{
Name = "FulfillOrder", Seq = 3, IsFinish = true,
Actions = { new FulfillOrderAction() }
}
},
Triggers = new List<WorkFlowTrigger>
{
new WorkFlowTrigger { Kind = TriggerKind.EventDriven,
EventTopic = "orders.created" },
new WorkFlowTrigger { Kind = TriggerKind.Scheduled,
CronExpression = "0 */6 * * *" } // Every 6 hours
}
};
// Link steps
workflow.Datasteps[0].NextStep.Add(workflow.Datasteps[1]);
workflow.Datasteps[1].PrevStep = workflow.Datasteps[0];
workflow.Datasteps[1].NextStep.Add(workflow.Datasteps[2]);
workflow.Datasteps[2].PrevStep = workflow.Datasteps[1];
Execute with Context
var engine = new WorkFlowEditor(editor);
var result = await engine.ExecuteAsync(workflow, new WorkFlowContext
{
Data = order,
Parameters = new Dictionary<string, object>
{
["UserId"] = userId,
["Source"] = "web",
["Priority"] = "high"
}
});
if (result.Success)
{
Console.WriteLine($"Completed in {result.Duration}");
foreach (var step in result.StepResults)
Console.WriteLine($" {step.StepName}: OK");
}
else
{
Console.WriteLine($"Failed at: {result.FailedStep}");
Console.WriteLine($"Error: {result.Error?.Message}");
}
Custom Action
public class ValidateOrderAction : WorkFlowAction
{
public override PassedArgs PerformAction(
IProgress<PassedArgs> progress, CancellationToken token)
{
return PerformAction(progress, token, args =>
{
var order = InParameters[0].ParameterObject as Order;
if (order.Total <= 0)
{
args.Messege = "Invalid order total";
args.ParameterInt1 = -1;
}
else
{
args.Messege = "Order validated";
args.ParameterInt1 = 100;
}
return args;
});
}
}
Validation
var editor = new WorkFlowEditor(dmeEditor);
if (!editor.ValidateWorkflow(workflow, out var errors))
{
Console.WriteLine("Workflow validation failed:");
foreach (var error in errors)
Console.WriteLine($" - {error}");
return;
}
var result = await editor.ExecuteAsync(workflow, context);