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 Name | Type | Required | Default | Description |
|---|---|---|---|---|
| Action | ActivityAction | No | Execute | Execution timing |
| IsSynchronous | bool | No | true | Whether 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 entryEnter: Executed during EnterAsyncExecute: Executed during ProcessAsync (Default)QueryExit: Executed during CanExitAsync, can prevent node exitExit: Executed during ExitAsync
Notes:
QueryEnterandQueryExitcan prevent the flow from continuing by returning trueExecuteis 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 continuingfalse: 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
-
QueryEnter (Query Before Enter)
- Executed in CanEnterAsync
- If returns true, prevents node entry
- Used for pre-entry condition checks
-
Enter (On Enter)
- Executed in EnterAsync
- Executes action when the node is entered
- Used for initialization operations
-
Execute (On Execute)
- Executed in ProcessAsync
- The most common execution timing
- Used for main business logic
-
QueryExit (Query Before Exit)
- Executed in CanExitAsync
- If returns true, prevents node exit
- Used for pre-exit condition checks
-
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 Name | Description | When to Implement |
|---|---|---|
| ExecuteActionAsync | Execute specific action logic | Must Implement, this is an abstract method |
Methods Overridable
| Method Name | Purpose | When to Override |
|---|---|---|
| InvokeAsync | Control how the action is invoked | When custom invocation logic is needed |
Custom Action Node Development Steps
- Create Node Class and Inherit ActionActivity
[Serializable]
[Design("Node Name", "Node Description", Sort = Sort Order)]
[Category("Category Name")]
public class MyActionActivity : ActionActivity
{
// Implementation code
}
- Add Configuration Properties
[Design("Property Name", "Property Description", Sort = Sort Order)]
[Category("Category Name")]
[DataMember]
public string MyProperty { get; set; }
- 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);
}
- 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
Related Nodes
- BusinessActivity: Parent class, provides basic functionality
- ApiCallActivity: Inherits from ActionActivity, used for API calls
- MethodCallActivity: Inherits from ActionActivity, used for method calls
- ScriptCallActivity: Inherits from ActionActivity, used for script execution
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:
- Set Action = QueryEnter or QueryExit
- Throw an exception in ExecuteActionAsync
- The exception causes CanEnterAsync or CanExitAsync to return false
Update History
| Date | Version | Description |
|---|---|---|
| 2025-11-28 | 1.0 | Initial Release |
Last updated: 2025-11-28