Skip to main content
Version: Next

BusinessActivity

Category: Common Steps
Namespace: CMS.Plugin.FlowManagement.Abstractions.FlowBusiness.Activitys
Base Class: Activity (SYC.Flow.Kernel)
Module: FlowManagement.Abstractions

Overview​

BusinessActivity is the base class for all business flow nodes in the LMES flow system. It inherits from SYC.Flow.Kernel.Activity and provides basic functionality and lifecycle management for flow nodes, including complete flow control for node entry, execution, and exit, as well as flow context data management, exception handling, and logging.

All custom flow nodes should inherit from BusinessActivity or its subclasses (such as ActionActivity, ProcessBusinessActivity) to obtain complete flow management capabilities.

Business Scenarios​

Applicable Scenarios​

  • Use as Base Class: Base class for all custom flow nodes
  • Flow Control: Provides node lifecycle management (Enter, Execute, Exit)
  • Data Management: Manages read/write of flow context data
  • Exception Handling: Unified exception handling and error recovery mechanism
  • Logging: Standardized logging functionality

Role in System​

BusinessActivity is the core foundation class of the entire flow system. It:

  • Defines the standard lifecycle of flow nodes
  • Provides access interface for flow context (Flow.DataItems)
  • Implements unified exception handling mechanism
  • Supports publishing and subscribing to flow events
  • Provides extended property mechanism supporting dynamic configuration

Collaboration with Other Nodes​

  • ActionActivity: Inherits from BusinessActivity, adds execution timing control
  • ProcessBusinessActivity: Inherits from BusinessActivity, specifically for MES process business
  • All Business Nodes: Directly or indirectly inherit from BusinessActivity

Configuration Description​

Basic Configuration​

Property NameTypeRequiredDefaultDescription
TaskDelayintNo0Execute delay time (Unit: ms)
IsDisabledboolNofalseMask business code ProcessAsync() in the step, will skip business and continue processing
PublishFlowEventboolNofalseWhether to publish flow events
PublishDeliveryEventboolNofalseWhether to publish delivery events
IsPrintConsumptionboolNotrueWhether to print execution time consumption
StopwatchTypeStopwatchTypeNoCommonTiming type (Common/Start/Stop/Reset/Restart)
ExtendedPropertyBusinessPropertyCollectionNoEmpty CollectionStep extended properties
FlowExceptionMaxCountintNo10Flow exception limit count
SaveFlowInstanceboolNofalseSave instance to database

Configuration Details​

TaskDelay​

Description: Sets the delay time before node execution, in milliseconds. Waits for the specified time at the beginning of the ExecuteAsync method.

Value Range: Integer greater than or equal to 0

Notes:

  • Responds to flow cancellation requests during delay
  • Excessive delay may affect flow response performance
  • Usually used for debugging or special timing control scenarios

IsDisabled​

Description: When set to true, the node's ProcessAsync method will be skipped, but the node's lifecycle methods (EnterAsync, ExitAsync) will still execute.

Value Range: true or false

Notes:

  • Can be used to temporarily disable the business logic of a node
  • Flow context data will still be passed normally
  • Can be dynamically controlled via flow context: {NodeName}_IsActivityDisabled (NodeName is the value of the node's Name property)

PublishFlowEvent​

Description: Whether to publish a flow event (ProcessFlowEto) after node execution, which other modules can subscribe to.

Value Range: true or false

Notes:

  • The event contains all data of the current flow context
  • Synchronous or asynchronous publishing can be controlled via EventSynchronous property
  • Frequent event publishing may affect performance

ExtendedProperty​

Description: Extended property collection, allowing custom properties to be added to the node, which can be written to the flow context.

Value Range: BusinessPropertyCollection object

Notes:

  • Each extended property can be configured to write to flow context (WriteIntoDataItems)
  • Values of extended properties will automatically have "_Value" suffix added as flow context keys
  • Supports dynamic configuration at runtime

FlowExceptionMaxCount​

Description: Flow exception count limit. When the flow exception count exceeds this value, the node will refuse entry.

Value Range: Integer greater than or equal to -1, -1 means no limit

Notes:

  • Used to prevent flow from entering infinite exception loops
  • Exception counting is statistical based on flow name (ProcName)
  • Requires restarting flow or clearing exception count after reaching the limit

Flow Context​

Input Parameters​

Parameter NameTypeDescription
{NodeName}_IsActivityDisabledboolDynamically control whether node is disabled
ApplicationDataobjectApplication data (such as ProcessModel)
Custom ParametersanyBusiness data written by upstream nodes

Output Parameters​

Parameter NameTypeDescription
Extended Property ValueanyProperty values configured in ExtendedProperty
Custom ParametersanyBusiness data written in ProcessAsync

Data Flow Description​

  1. Read Data: Read flow context data via Flow.DataItems[key]
  2. Write Data: Write flow context data via Flow.DataItems[key] = value
  3. Helper Methods:
    • GetFlowItemValue(key): Supports dot-separated property paths and array indices
    • GetVariableValue(key): Supports variable reference in {key} format
    • ToFlowItemKey(key): Automatically adds "_Value" suffix

Business Logic Description​

Processing Flow​

The complete lifecycle of BusinessActivity includes the following steps:

  1. CanEnterAsync: Determine if node can be entered

    • Check if flow exception count exceeds limit
    • Check if flow is cancelled
    • Check if flow status is normal
  2. EnterAsync: Enter node

    • Create work item (FlowItem)
    • Record start time
    • Update current task information
    • Record entry log
  3. ExecuteAsync: Execute node

    • Execute delay (if TaskDelay is configured)
    • Call ProcessAsync to execute business logic
    • Publish flow event (if enabled)
    • Process extended properties
  4. ProcessAsync: Business logic processing

    • Default implementation is empty, overridden by subclasses
  5. CanExitAsync: Determine if node can be exited

    • Default returns true
  6. ExitAsync: Exit node

    • Record end time
    • Save flow instance (if enabled)
    • Print execution time consumption
    • Handle timer operations

Flow Chart​

[CanEnterAsync]
↓ (true)
[EnterAsync]
↓
[ExecuteAsync]
↓
[ProcessAsync] (Business Logic)
↓
[CanExitAsync]
↓ (true)
[ExitAsync]
↓
[Next Node]

Dependency Services​

Service InterfacePurposeDescription
ILmesMetricsPerformance metrics collectionOptional, used to collect flow execution metrics
ILocalEventBusEvent busUsed to publish flow events and delivery events
Flow.ServiceProviderService providerAccess services in dependency injection container

Exception Handling​

Exception TypeHandling MethodDescription
BusinessExceptionMark flow status as UnexpectedBusiness exception in ProcessAsync
TaskCanceledExceptionRecord error logFlow cancelled
OperationCanceledExceptionRecord error logOperation cancelled
Other ExceptionsRecord exception log and mark flow statusUnexpected exceptions

Exception Handling Mechanism:

  • Exceptions in ProcessAsync are captured and wrapped as BusinessException
  • Exceptions increase flow exception count
  • Node refuses entry after reaching exception limit
  • Exceptions trigger flow reset (ResetEventAsync)

Logging​

BusinessActivity records the following key logs:

  • Enter Node: Enter, TaskID={taskId}
  • Execute Delay: Exceptions during delay
  • Business Exception: Exception details in ProcessAsync
  • Flow Event: Start and end of publishing flow events
  • Exit Node: Exit, Time Consumption={milliseconds}ms
  • Timeout Warning: Recorded when execution time exceeds TimeOut

Usage Examples​

Basic Example​

// Simplest custom node
[Serializable]
[Design("Simple Node", "Simple business node example", Sort = 1)]
[Category("Custom")]
public class SimpleActivity : BusinessActivity
{
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// Read input data
var input = Flow.DataItems["InputData"]?.ToString();

// Process business logic
var result = $"Processed: {input}";

// Write output data
Flow.DataItems["OutputData"] = result;

// Record log
Flow.Logger.LogMessage($"Processing complete: {result}", Name);

await base.ProcessAsync(args);
}
}

Advanced Example​

// Node with configuration properties and exception handling
[Serializable]
[Design("Advanced Node", "Business node example with configuration", Sort = 2)]
[Category("Custom")]
public class AdvancedActivity : BusinessActivity
{
[Design("Config Item 1", "First configuration item", Sort = 1)]
[Category("Configuration")]
[DataMember]
public string Config1 { get; set; }

[Design("Config Item 2", "Second configuration item", Sort = 2)]
[Category("Configuration")]
[DataMember]
[Required]
public int Config2 { get; set; }

public override List<FlowItemKey> FlowItemKeys => new()
{
new FlowItemKey("Input_Value", "Input Parameter", "Input parameter description", typeof(string)),
new FlowItemKey("Output_Value", "Output Parameter", "Output parameter description", typeof(string))
};

public override async Task ProcessAsync(ProcessflowEventArgs args)
{
try
{
// Read input
var input = GetFlowItemValue("Input")?.ToString();
if (string.IsNullOrEmpty(input))
{
Flow.Logger.LogWarningMessage("Input parameter is empty", Name);
return;
}

// Use configuration items
Flow.Logger.LogMessage($"Config1={Config1}, Config2={Config2}", Name);

// Call external service
var service = Flow.ServiceProvider.GetRequiredService<IMyService>();
var result = await service.ProcessAsync(input, Config2);

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

Flow.Logger.LogMessage($"Processing success: {result}", Name);
}
catch (Exception ex)
{
Flow.Logger.LogExceptionMessage(ex, Name);
throw;
}

await base.ProcessAsync(args);
}
}

Complete Flow Example​

{
"Name": "Example Flow",
"Activities": [
{
"Type": "SimpleActivity",
"Name": "Start Node",
"Alias": "Start",
"ExtendedProperty": [
{
"Name": "InputData",
"Value": "Initial Data",
"WriteIntoDataItems": true
}
]
},
{
"Type": "AdvancedActivity",
"Name": "Process Node",
"Alias": "Process",
"Config1": "ConfigValue1",
"Config2": 100,
"TaskDelay": 1000,
"IsPrintConsumption": true
},
{
"Type": "EndActivity",
"Name": "End Node",
"Alias": "End"
}
],
"Transitions": [
{
"From": "Start",
"To": "Process"
},
{
"From": "Process",
"To": "End"
}
]
}

Extension Development Guide​

Inheritance Hierarchy​

Activity (SYC.Flow.Kernel)
└── BusinessActivity
├── ActionActivity (Action Node Base Class)
│ ├── ApiCallActivity
│ ├── MethodCallActivity
│ └── ScriptCallActivity
├── ProcessBusinessActivity (Process Node Base Class)
│ ├── GenerateCodeActivity
│ ├── MaterialDetectionActivity
│ └── PrintLabelActivity
└── Other Custom Nodes

Overridable Methods​

Method NamePurposeWhen to Override
CanEnterAsyncDetermine if node can be enteredNeed custom entry conditions (e.g., check prerequisites)
EnterAsyncExecuted when entering nodeNeed initialization operations (e.g., initialize variables, create resources)
ProcessAsyncCore business logicMust Override, implement specific business functionality
CanExitAsyncDetermine if node can be exitedNeed custom exit conditions (e.g., verify processing results)
ExitAsyncExecuted when exiting nodeNeed cleanup operations (e.g., release resources, save state)
ValidateValidate node configurationNeed custom configuration validation logic

Custom Node Development Steps​

  1. Create Node Class
[Serializable]
[Design("Node Name", "Node Description", Sort = Sort Order)]
[Category("Category Name")]
public class MyCustomActivity : BusinessActivity
{
// Implementation code
}
  1. Add Configuration Properties
[Design("Property Name", "Property Description", Sort = Sort Order)]
[Category("Category Name")]
[DataMember]
[Required] // Optional: Mark as required
public string MyProperty { get; set; }
  1. Define Flow Context Keys
public override List<FlowItemKey> FlowItemKeys => new()
{
new FlowItemKey("MyInput_Value", "Input", "Input Description", typeof(string)),
new FlowItemKey("MyOutput_Value", "Output", "Output Description", typeof(string))
};
  1. Implement Business Logic
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// 1. Read input
var input = GetFlowItemValue("MyInput")?.ToString();

// 2. Process business
var result = await DoBusinessLogic(input);

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

// 4. Record log
Flow.Logger.LogMessage($"Processing complete", Name);

await base.ProcessAsync(args);
}

Registration and Integration​

  1. Compile Node:

    • Compile custom node into DLL
    • Ensure necessary dependency packages are referenced
  2. Deploy Node:

    • Place DLL in LMES plugin directory
    • Or integrate directly into main project
  3. Configure Node:

    • New node visible in flow designer
    • Information configured via Design attribute will be displayed in designer
  4. Test Node:

    • Create test flow
    • Run flow to verify node functionality
    • Check log output

Notes​

  • ⚠️ Async Methods: All lifecycle methods are asynchronous, must use async/await correctly
  • ⚠️ Exception Handling: Exceptions in ProcessAsync are automatically captured, but proper handling should still be done
  • ⚠️ Flow Cancellation: Long-running operations should check Flow.CancellationTokenSource.Token
  • ⚠️ Thread Safety: Nodes may execute in multi-threaded environments, pay attention to thread safety
  • ⚠️ Performance: Avoid executing time-consuming synchronous operations in ProcessAsync
  • 💡 Best Practices:
    • Use GetFlowItemValue instead of accessing Flow.DataItems directly to support complex paths
    • Make full use of logging for easy troubleshooting
    • Use extended properties reasonably to improve node flexibility
    • Validate input parameters at the beginning of ProcessAsync

FAQ​

Q1: How to pass data between nodes?​

A: Use flow context (Flow.DataItems):

// Write data
Flow.DataItems["MyKey_Value"] = myValue;

// Read data
var value = Flow.DataItems["MyKey_Value"];

Q2: How to access injected services?​

A: Use Flow.ServiceProvider:

var myService = Flow.ServiceProvider.GetRequiredService<IMyService>();

Q3: How to handle node execution failure?​

A: Throw an exception in ProcessAsync, the system will handle it automatically:

if (!success)
{
throw new BusinessException("MyNode", "Reason for failure");
}

Q4: How to dynamically disable a node?​

A: Set the disabled flag in the flow context:

Flow.DataItems["MyNode_IsActivityDisabled"] = true;

Update History​

DateVersionDescription
2025-11-281.0Initial Release

Last updated: 2025-11-28