Skip to main content
Version: Next

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 NameTypeRequiredDefaultDescription
ExecuterNamestringYes-Full class name of the executor object (including namespace)
ActionActionTypeNoExecuteExecution timing
IsSynchronousboolNotrueWhether 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​

  1. Get all IMethodExecuter implementations from the dependency injection container.
  2. Find the executor whose class name matches ExecuterName.
  3. Call the executor's Execute method.
  4. Write the returned data items to the process context.
  5. Record logs.

Dependent Services​

Service InterfaceUsage
IMethodExecuterCustom method executor interface
Flow.ServiceProviderGet 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​

  1. Create executor class, implement IMethodExecuter interface.
  2. Implement Execute method, write business logic.
  3. Register to DI container.
  4. 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.

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​

DateVersionDescription
2025-11-281.0Initial version

Last updated: 2025-11-28