Activity
An activity is a stable state where a process instance waits. Activities can have actions that run on entry, exception handlers for error recovery, timeouts that limit execution or idle time, and flags that disable database persistence. Use SetStateAsync and SetActivityWithExecutionAsync to move a process instance programmatically without going through normal transitions.
An activity is a stable state where a process instance waits. Activities are defined in a scheme. Every scheme has exactly one initial activity where new process instances start and can have zero or more activities marked as final. A process instance leaves an activity when a command executes, a timer fires, or an automatic transition fires. A process instance is always positioned at exactly one activity at a time.
During normal execution, when a process instance enters an activity, the activity can execute useful work through actions. Actions are blocks of code that run automatically upon entry, such as sending notifications, updating business data, or calling external services. Actions are attached to the activity via the Implementation property.
Activity properties
Each activity has a set of properties that define its identity, behavior, and position in the scheme.
Core properties
These are the most commonly configured properties:
| Property | Type | Description |
|---|---|---|
| Name | string | Case-sensitive, unique identifier for the activity within the scheme |
| State | string | The business state name associated with this activity; non-unique or absent |
| IsInitial | bool | Only one activity per scheme may be initial |
| IsFinal | bool | Marks a final activity; optional in regular schemes and required in schemes used for inlining |
| IsForSetState | bool | Marks this activity as the target when moving a process to a state by name. Only one activity per state value should have this enabled |
| Implementation | list of ActionDefinitionReference | Actions that execute when a process instance enters this activity |
Scenario-specific properties
These properties are not always needed. Some are used in specialized scenarios such as disabling database persistence, setting timeouts, or handling errors. Others are metadata helpers:
| Property | Type | Description |
|---|---|---|
| IsAutoSchemeUpdate | bool | Allows the process instance to be updated to a new scheme version when it reaches this activity |
| UserComment | string | Optional custom comment attached to the activity |
| Annotations | list of Annotation | Key-value metadata attached to the activity; see Annotations |
| DisablePersistState | bool | Skips saving the current state of the process instance when entering this activity |
| DisablePersistTransitionHistory | bool | Skips saving the transition history entry when passing through this activity |
| DisablePersistParameters | bool | Skips saving the parameter values when entering this activity |
| ExecutionTimeout | ActivityTimeout | Limits activity execution time and cancels it when the interval expires; asynchronous actions must use the provided CancellationToken |
| IdleTimeout | ActivityTimeout | Limits how long the process instance may sit in this activity without any transition |
| ExceptionsHandlers | list of ActivityExceptionsHandler | Configures what should happen if an action throws an exception |
| PreExecutionImplementation | list of ActionDefinitionReference | Actions executed only during pre-execution simulation |
Exception handling
When an action on an activity throws an exception, you can add exception handlers to the activity. If no handler processes the exception, it reaches the normal workflow error handling.
Each handler has two parts: which exceptions to catch and what to do when one occurs. The runtime evaluates handlers by ascending Order and uses the first matching handler. To catch a specific exception, use its full type name such as System.InvalidOperationException. To catch any exception, use *, and place that handler after more specific handlers.
Four actions are available when an exception is caught:
- SetActivity - move the process instance to another activity. You specify which activity by name.
- SetState - move the process instance to a business state. The runtime finds the activity that matches that state.
- Retry - run the activity implementation again. You specify how many times to retry before giving up.
- Ignore - suppress the exception and continue workflow execution.
<Activity Name="Review"> <Implementation> <ActionRef Order="0" NameRef="SendNotification"/> </Implementation> <ExceptionsHandlers> <ExceptionsHandler Exceptions="System.InvalidOperationException" Type="SetActivity" NameForSet="Failed" Order="1"/> <ExceptionsHandler Exceptions="*" Type="Retry" RetryCount="3" Order="2"/> </ExceptionsHandlers></Activity>In this example, InvalidOperationException reaches the handler with Order="1", so the process moves to the Failed activity. Any other exception reaches the wildcard handler with Order="2" and retries the activity implementation up to three times.
Execution and idle timeouts
An activity can have two timeouts that limit how long things can take.
- ExecutionTimeout - requests cancellation when activity execution exceeds the configured interval, then applies the configured result.
- IdleTimeout - limits how long the process instance can sit at this activity without any transition firing. If the timer expires, the runtime takes the configured action.
ExecutionTimeout cancels execution through the CancellationToken passed to asynchronous actions and custom activity execution. It cannot stop synchronous code or asynchronous code that ignores the token; the runtime waits for that code to finish before handling the timeout.
Each timeout defines an interval and what to do when it expires. ExecutionTimeout supports SetActivity, SetState, and Retry. IdleTimeout supports SetActivity and SetState.
IdleTimeout is implemented as an interval timer and supports work calendars when its value uses calendar-aware units such as 2wd or 4 whours. ExecutionTimeout parses a regular elapsed-time interval directly and does not use work calendars.
<Activity Name="Review"> <IdleTimeout Timer="2d" Type="SetState" NameForSet="Escalated"/></Activity>In this example, if the process stays at the Review activity for more than 2 days without advancing, the runtime moves it to the Escalated state.
Disabling persistence
By default, the runtime saves the process state, its parameters, and the transition history to the database through the persistence provider every time the process moves. For high-throughput scenarios where every millisecond counts, you can skip some of these saves on a per-activity basis:
DisablePersistState- skip saving the current state (activity and status) for this activity.DisablePersistParameters- skip saving the parameter values for this activity.DisablePersistTransitionHistory- skip saving the transition history entry when moving through this activity.
These flags do not affect reads - the runtime still loads state and parameters from the database when needed. They only disable the write that happens after each transition. Use them on high-traffic activities where you are willing to lose the latest state data in exchange for faster execution.
Pre-execution implementation
An activity can have two sets of actions: the regular Implementation that runs during normal execution and PreExecutionImplementation that runs only during pre-execution simulation. During simulation, the runtime uses PreExecutionImplementation instead of Implementation for a visited activity.
Pre-execution actions should implement simulation-specific behavior. They can still call external systems, so adding an action to PreExecutionImplementation does not make that action free of side effects.
<Activity Name="Review"> <PreExecutionImplementation> <ActionRef Order="0" NameRef="PreFetchDocument"/> </PreExecutionImplementation> <Implementation> <ActionRef Order="0" NameRef="SendNotification"/> </Implementation></Activity>What is State
The State property represents the business state, which is the human-readable status such as "Draft", "Under Review", "Approved". An Activity is the physical state that the runtime tracks internally. One State can be shared across multiple activities; for example, "Under Review" can be the state for both a ManagerReview activity and a DirectorReview activity.
Declaring activities in a scheme
Activities are listed in the <Activities> section of a scheme. Each <Activity> element defines one waiting point in the process. Activities are connected by transitions, which determine the paths a process instance can take from one activity to another.
The activity itself does not execute logic. It is a named waiting point that holds the process instance until an outgoing transition fires. Actions run when the process instance arrives at an activity. Each activity can be configured to perform specific work, such as sending notifications, changing data, or calling an external service. This work is defined by the activity's actions.
<Activities> <Activity Name="Draft" IsInitial="true"/> <Activity Name="Review"/> <Activity Name="Approved" IsFinal="true"/> <Activity Name="Rejected" IsFinal="true"/></Activities>IsInitial="true" marks the activity where Workflow Engine positions new instances. IsFinal="true" is a marker: if execution stops in that activity, the runtime sets the process status to Finalized. The marker does not prohibit outgoing transitions. A regular scheme may have no final activities, but a scheme used for inlining must have at least one so the inlined fragment can connect back to its parent scheme.
Managing activities from code
You can move a process instance to a specific activity or state programmatically without going through a normal transition. These methods are useful for error recovery, manual overrides, and testing.
SetState method
SetStateAsync selects an activity whose State matches the requested name and whose IsForSetState property is true. By default, the method executes that activity's Implementation and follows its automatic transitions, bypassing normal incoming transition conditions:
var setStateParams = new SetStateParams(processId, "Approved");await runtime.SetStateAsync(setStateParams);SetActivity method
Moves the process to a specific activity directly, without requiring its State or IsForSetState properties. You have two options:
SetActivityWithExecutionAsync- moves and runs the activity's actions.SetActivityWithoutExecutionAsync- moves without running actions.
var scheme = processInstance.ProcessScheme;var activity = scheme.FindActivity("Review");await runtime.SetActivityWithExecutionAsync( "user-42", null, null, activity, processInstance);Resume process
ResumeAsync continues from the current activity, or from the activity named in ResumeParams, without executing that starting activity's Implementation again. It evaluates the activity's outgoing automatic transitions and follows one when its conditions pass. Unlike SetStateAsync, ResumeAsync does not select a target by business state or execute the selected starting activity:
var result = await runtime.ResumeAsync(processId);Finding activities in a scheme
Every process instance holds a reference to its scheme. Use it to look up activities by name or find the starting point:
var scheme = processInstance.ProcessScheme;// Find the initial activityvar start = scheme.InitialActivity;// Find a specific activity by namevar review = scheme.FindActivity("Review");// List all activitiesList<ActivityDefinition> allActivities = scheme.Activities;FindActivity throws an exception if no activity with that name exists.
See also
Transition
Transitions connect activities and define what moves a process instance forward.
Command
Commands trigger transitions from an activity.
Scheme
A scheme organizes activities and transitions into a process definition.
Process Instance
A process instance moves through activities and tracks its current position.
Frequently asked questions
What is an activity in Workflow Engine?
An activity is a named stable state in a scheme where a process instance waits until a command, timer, or automatic transition moves it forward. Every scheme has exactly one initial activity and may have zero or more activities marked as final.
Can multiple activities share the same state?
Yes. Multiple activities can have the same State value. For example, both a ManagerReview and a DirectorReview activity can use the "Under Review" state. The activity name identifies the physical waiting point, while the state is the business status.
Where are activities declared in a scheme?
Activities are declared in the <Activities> section of the scheme XML. Each <Activity> element defines one waiting point, and activities are connected by transitions that determine how a process instance moves from one to another.