MethodCallActivity
Category: Common Steps Namespace: CMS.Plugin.FlowManagement.Domain.FlowBusiness.Activitys Base Class: ActionActivity Module: FlowManagement.Domain
Overview
MethodCallActivity (CSharp Call) is used to call custom C# methods within a process. By implementing the IMethodExecuter interface, developers can write custom business logic and call it in the process through this node. This is a powerful extension mechanism that allows executing complex business logic in the process without modifying the process engine itself.
Business Scenarios
Applicable Scenarios
- Complex Business Logic: Need to execute complex calculations or business rules.
- System Integration: Call services or components of internal systems.
- Data Processing: Perform complex transformations or processing on process data.
- Custom Functions: Implement functions not directly supported by process nodes.
- Code Reuse: Encapsulate common business logic as reusable executors.
Configuration Description
Basic Configuration
| Property Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ExecuterName | string | Yes | - | Full class name of the executor object (including namespace) |
| Action | ActionType | No | Execute | Execution timing |
| IsSynchronous | bool | No | true | Whether to execute synchronously |
ExecuterName
Description: The full class name (including namespace) of the IMethodExecuter implementation class, such as "MyApp.Executers.MyCustomExecuter".
Notes:
- Must be an IMethodExecuter implementation registered in the dependency injection container.
- Use the full class name (namespace.classname).
- The executor must be registered at application startup.
Process Context
Input Parameters
Access all process context data via ProcessflowEventArgs.
Output Parameters
All key-value pairs in the dictionary returned by the executor will be written to the process context.
Business Logic Description
Processing Flow
- Get all IMethodExecuter implementations from the dependency injection container.
- Find the executor whose class name matches ExecuterName.
- Call the executor's Execute method.
- Write the returned data items to the process context.
- Record logs.
Dependent Services
| Service Interface | Usage |
|---|---|
| IMethodExecuter | Custom method executor interface |
| Flow.ServiceProvider | Get registered executors |
Usage Examples
Basic Example
{
"Type": "MethodCallActivity",
"Name": "Calculate Discount",
"Alias": "CalculateDiscount",
"ExecuterName": "MyApp.Executers.DiscountCalculator",
"Action": "Execute"
}
Implementing Custom Executor
using CMS.Plugin.FlowManagement.Abstractions;
using SYC.Flow.Kernel;
namespace MyApp.Executers
{
public class DiscountCalculator : IMethodExecuter
{
public string ExecuterName => "Discount Calculator";
public string ExecuterDescription => "Calculates discount based on order amount";
public async Task<Dictionary<string, object>> Execute(ProcessflowEventArgs args)
{
var result = new Dictionary<string, object>();
// Read data from process context
var orderAmount = Convert.ToDecimal(args.DataItems["OrderAmount_Value"]);
var customerLevel = args.DataItems["CustomerLevel_Value"]?.ToString();
// Execute business logic
decimal discount = customerLevel switch
{
"VIP" => orderAmount * 0.2m,
"Gold" => orderAmount * 0.15m,
"Silver" => orderAmount * 0.1m,
_ => 0m
};
decimal finalAmount = orderAmount - discount;
// Return result
result["Discount_Value"] = discount;
result["FinalAmount_Value"] = finalAmount;
return await Task.FromResult(result);
}
}
}
Registering Executor
// Register in Startup.cs or Program.cs
services.AddTransient<IMethodExecuter, DiscountCalculator>();
services.AddTransient<IMethodExecuter, TaxCalculator>();
services.AddTransient<IMethodExecuter, ShippingCalculator>();
Complex Executor Example
public class OrderProcessor : IMethodExecuter
{
private readonly IOrderService _orderService;
private readonly IInventoryService _inventoryService;
private readonly ILogger<OrderProcessor> _logger;
public OrderProcessor(
IOrderService orderService,
IInventoryService inventoryService,
ILogger<OrderProcessor> logger)
{
_orderService = orderService;
_inventoryService = inventoryService;
_logger = logger;
}
public string ExecuterName => "Order Processor";
public string ExecuterDescription => "Handles order creation and inventory deduction";
public async Task<Dictionary<string, object>> Execute(ProcessflowEventArgs args)
{
var result = new Dictionary<string, object>();
try
{
// Get order info
var orderId = args.DataItems["OrderId_Value"]?.ToString();
var productId = args.DataItems["ProductId_Value"]?.ToString();
var quantity = Convert.ToInt32(args.DataItems["Quantity_Value"]);
// Check inventory
var available = await _inventoryService.CheckAvailability(productId, quantity);
if (!available)
{
result["ProcessResult_Value"] = "Failed";
result["ErrorMessage_Value"] = "Insufficient inventory";
return result;
}
// Create order
var order = await _orderService.CreateOrder(orderId, productId, quantity);
// Deduct inventory
await _inventoryService.DeductInventory(productId, quantity);
result["ProcessResult_Value"] = "Success";
result["OrderNumber_Value"] = order.OrderNumber;
result["OrderStatus_Value"] = order.Status;
_logger.LogInformation($"Order processed successfully: {order.OrderNumber}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Order processing failed");
result["ProcessResult_Value"] = "Failed";
result["ErrorMessage_Value"] = ex.Message;
}
return result;
}
}
Extension Development Guide
IMethodExecuter Interface
public interface IMethodExecuter
{
string ExecuterName { get; }
string ExecuterDescription { get; }
Task<Dictionary<string, object>> Execute(ProcessflowEventArgs args);
}
Development Steps
- Create executor class, implement IMethodExecuter interface.
- Implement Execute method, write business logic.
- Register to DI container.
- Configure ExecuterName in the process.
Best Practices
- Use dependency injection to get required services.
- Use "_Value" suffix for returned dictionary keys.
- Record detailed logs.
- Handle exceptions and return error messages.
- Keep executor responsibilities single.
Notes
- ⚠️ Class name must be complete: Full class name including namespace.
- ⚠️ Must Register: Executor must be registered in DI container.
- ⚠️ Exception Handling: Executor should handle exceptions internally.
- ⚠️ Return Value: Returned dictionary will be written directly to process context.
- 💡 Recommendations:
- Write unit tests for executors.
- Use meaningful ExecuterName and Description.
- Avoid executing time-consuming operations in executors.
Related Nodes
- ActionActivity: Base class.
- ApiCallActivity: API call.
- ScriptCallActivity: Script call.
FAQ
Q1: How to pass parameters to the executor?
A: Access all data in the process context via ProcessflowEventArgs.DataItems.
Q2: How does the executor return data?
A: Return Dictionary<string, object>, all key-value pairs will be written to the process context.
Q3: What if the executor is not found?
A: Check: 1) Is the class name correct (including namespace)? 2) Is it registered in the DI container?
Q4: Can other services be called in the executor?
A: Yes, inject required services via the constructor.
Update History
| Date | Version | Description |
|---|---|---|
| 2025-11-28 | 1.0 | Initial version |
Last updated: 2025-11-28