Skip to main content
Version: Next

ActionActivity

Category: Common Steps
Namespace: CMS.Plugin.FlowManagement.Abstractions.FlowBusiness.Activitys
Base Class: BusinessActivity
Module: FlowManagement.Abstractions

Overview​

ActionActivity is the abstract base class for action nodes, inheriting from BusinessActivity. It provides the ability to execute actions at different timings in the flow node lifecycle, allowing developers to precisely control the execution timing of actions (Before Enter, On Enter, On Execute, Before Exit, On Exit).

All nodes that need to execute external actions at specific timings (such as API calls, script execution, method calls, etc.) should inherit from ActionActivity.

Business Scenarios​

Applicable Scenarios​

  • API Call: Call external APIs at specific timings
  • Script Execution: Execute scripts at different stages of the flow
  • Method Call: Call custom C# methods
  • External System Integration: Interact with external systems
  • Event Trigger: Trigger external events at specific timings

Role in System​

As the base class for action nodes, ActionActivity provides:

  • Timing Control: Control the execution timing of actions via the Action property
  • Sync/Async: Support synchronous and asynchronous execution modes
  • Exception Handling: Unified exception handling mechanism
  • Execution Control: Control whether the flow continues via return values

Collaboration with Other Nodes​

  • BusinessActivity: Inherits from BusinessActivity, possessing all basic functions
  • ApiCallActivity: Inherits from ActionActivity, used for API calls
  • MethodCallActivity: Inherits from ActionActivity, used for method calls
  • ScriptCallActivity: Inherits from ActionActivity, used for script execution

Configuration Description​

Basic Configuration​

Property NameTypeRequiredDefaultDescription
ActionActivityActionNoExecuteExecution timing
IsSynchronousboolNotrueWhether to process synchronously

Note: ActionActivity inherits all configuration properties from BusinessActivity

Configuration Details​

Action​

Description: Controls the execution timing of the action in the node lifecycle.

Value Range: ActivityAction enum values

  • QueryEnter: Executed during CanEnterAsync, can prevent node entry
  • Enter: Executed during EnterAsync
  • Execute: Executed during ProcessAsync (Default)
  • QueryExit: Executed during CanExitAsync, can prevent node exit
  • Exit: Executed during ExitAsync

Notes:

  • QueryEnter and QueryExit can prevent the flow from continuing by returning true
  • Execute is the most commonly used execution timing
  • Choosing the appropriate timing allows for more precise flow control

IsSynchronous​

Description: Controls whether the action is executed synchronously or asynchronously.

Value Range: true or false

Notes:

  • true: Synchronous execution, waits for the action to complete before continuing
  • false: Asynchronous execution, does not wait for the action to complete, continues immediately
  • Synchronous execution captures exceptions and may prevent flow continuation
  • Asynchronous execution does not block the flow, but exception handling is limited

Flow Context​

ActionActivity itself does not define specific flow context keys; they are defined by subclasses based on specific functionality.

Input Parameters​

Defined by subclasses

Output Parameters​

Defined by subclasses

Business Logic Description​

Processing Flow​

ActionActivity executes actions at different lifecycle stages based on the Action property:

CanEnterAsync (Action=QueryEnter)
↓
EnterAsync (Action=Enter)
↓
ProcessAsync (Action=Execute)
↓
CanExitAsync (Action=QueryExit)
↓
ExitAsync (Action=Exit)

Execution Timing Details​

  1. QueryEnter (Query Before Enter)

    • Executed in CanEnterAsync
    • If returns true, prevents node entry
    • Used for pre-entry condition checks
  2. Enter (On Enter)

    • Executed in EnterAsync
    • Executes action when the node is entered
    • Used for initialization operations
  3. Execute (On Execute)

    • Executed in ProcessAsync
    • The most common execution timing
    • Used for main business logic
  4. QueryExit (Query Before Exit)

    • Executed in CanExitAsync
    • If returns true, prevents node exit
    • Used for pre-exit condition checks
  5. Exit (On Exit)

    • Executed in ExitAsync
    • Executes action when the node is exited
    • Used for cleanup operations

Synchronous vs Asynchronous Execution​

Synchronous Execution (IsSynchronous = true):

try
{
await ExecuteActionAsync(args);
}
catch (Exception ex)
{
cancel = true;
Flow.Logger.LogExceptionMessage(ex);
}

Asynchronous Execution (IsSynchronous = false):

_ = ExecuteActionAsync(args).ContinueWith(task =>
{
if (!task.IsCompletedSuccessfully && task.Exception != null)
{
cancel = true;
Flow.Logger.LogExceptionMessage(task.Exception);
}
});

Exception Handling​

  • During synchronous execution, exceptions are captured and logged
  • Exceptions cause the cancel flag to be set to true
  • During QueryEnter and QueryExit, cancel=true prevents flow continuation
  • During asynchronous execution, exception handling is done in the background

Usage Examples​

Basic Example - Custom Action Node​

[Serializable]
[Design("Custom Action", "Custom Action Node Example", Sort = 1)]
[Category("Custom")]
public class CustomActionActivity : ActionActivity
{
[Design("Action Parameter", "Action Parameter Description", Sort = 1)]
[Category("Configuration")]
[DataMember]
public string ActionParameter { get; set; }

protected override async Task ExecuteActionAsync(ProcessflowEventArgs args)
{
// Implement specific action logic
Flow.Logger.LogMessage($"Executing Action: {ActionParameter}", Name);

// Call external service
var service = Flow.ServiceProvider.GetRequiredService<IMyService>();
await service.DoSomethingAsync(ActionParameter);

// Write result to flow context
Flow.DataItems["ActionResult"] = "Success";
}
}

Advanced Example - Pre-entry Check​

[Serializable]
[Design("Condition Check Action", "Check condition before entry", Sort = 2)]
[Category("Custom")]
public class ConditionalActionActivity : ActionActivity
{
public ConditionalActionActivity()
{
// Set to execute before entry
Action = ActivityAction.QueryEnter;
}

[Design("Check Condition", "Condition to be met", Sort = 1)]
[Category("Configuration")]
[DataMember]
public string Condition { get; set; }

protected override async Task ExecuteActionAsync(ProcessflowEventArgs args)
{
// Check condition
var conditionValue = Flow.DataItems[Condition]?.ToString();

if (string.IsNullOrEmpty(conditionValue))
{
Flow.Logger.LogWarningMessage($"Condition {Condition} not met", Name);
// Returning true prevents node entry
throw new Exception("Condition not met");
}

Flow.Logger.LogMessage($"Condition {Condition} met", Name);
}
}

Complete Flow Example​

{
"Name": "Action Node Flow",
"Activities": [
{
"Type": "ConditionalActionActivity",
"Name": "Check Condition",
"Alias": "CheckCondition",
"Action": "QueryEnter",
"IsSynchronous": true,
"Condition": "RequiredData"
},
{
"Type": "CustomActionActivity",
"Name": "Execute Action",
"Alias": "ExecuteAction",
"Action": "Execute",
"IsSynchronous": true,
"ActionParameter": "Parameter Value"
},
{
"Type": "EndActivity",
"Name": "End",
"Alias": "End"
}
]
}

Extension Development Guide​

Inheritance Hierarchy​

Activity (SYC.Flow.Kernel)
└── BusinessActivity
└── ActionActivity
├── ApiCallActivity
├── MethodCallActivity
├── ScriptCallActivity
└── Custom Action Node

Methods to Implement​

Method NameDescriptionWhen to Implement
ExecuteActionAsyncExecute specific action logicMust Implement, this is an abstract method

Methods Overridable​

Method NamePurposeWhen to Override
InvokeAsyncControl how the action is invokedWhen custom invocation logic is needed

Custom Action Node Development Steps​

  1. Create Node Class and Inherit ActionActivity
[Serializable]
[Design("Node Name", "Node Description", Sort = Sort Order)]
[Category("Category Name")]
public class MyActionActivity : ActionActivity
{
// Implementation code
}
  1. Add Configuration Properties
[Design("Property Name", "Property Description", Sort = Sort Order)]
[Category("Category Name")]
[DataMember]
public string MyProperty { get; set; }
  1. Implement ExecuteActionAsync Method
protected override async Task ExecuteActionAsync(ProcessflowEventArgs args)
{
// 1. Read configuration and input
var input = Flow.DataItems["Input"]?.ToString();

// 2. Execute action logic
var result = await DoActionAsync(input, MyProperty);

// 3. Write output
Flow.DataItems["Output"] = result;

// 4. Log
Flow.Logger.LogMessage($"Action Completed: {result}", Name);
}
  1. Select Execution Timing
public MyActionActivity()
{
// Set execution timing in constructor
Action = ActivityAction.Execute; // or other timings
IsSynchronous = true; // or false
}

Notes​

  • ⚠️ Abstract Class: ActionActivity is an abstract class and cannot be instantiated directly
  • ⚠️ Must Implement: Subclasses must implement the ExecuteActionAsync method
  • ⚠️ Execution Timing: Choose the appropriate Action value based on business requirements
  • ⚠️ Sync/Async: Synchronous execution waits for completion, asynchronous execution does not block
  • ⚠️ Exception Handling: Exceptions during QueryEnter and QueryExit prevent flow continuation
  • 💡 Best Practices:
    • Use Action = Execute for most cases
    • Use QueryEnter or QueryExit when condition checks are needed
    • Consider asynchronous execution for long-running operations
    • Make full use of logging for debugging

FAQ​

Q1: When to use ActionActivity instead of BusinessActivity?​

A: When you need to:

  • Precisely control the execution timing of an action
  • Execute specific operations on entry or exit
  • Implement condition checks that can prevent flow continuation

Q2: What value should the Action property be set to?​

A:

  • Execute: Most common, runs when the node executes (default)
  • QueryEnter: When condition check is needed before entry
  • Enter: When initialization is needed on entry
  • QueryExit: When condition check is needed before exit
  • Exit: When cleanup is needed on exit

Q3: What is the difference between synchronous and asynchronous execution?​

A:

  • Synchronous (IsSynchronous=true): Waits for action completion, exceptions are captured
  • Asynchronous (IsSynchronous=false): Does not wait for completion, continues immediately, suitable for time-consuming operations

Q4: How to prevent the flow from continuing?​

A:

  1. Set Action = QueryEnter or QueryExit
  2. Throw an exception in ExecuteActionAsync
  3. The exception causes CanEnterAsync or CanExitAsync to return false

Update History​

DateVersionDescription
2025-11-281.0Initial Release

Last updated: 2025-11-28