Skip to content

Create a saga state machine

MassTransit’s saga state machines provide a C# syntax to define an event-driven state machine, including states, events, and behaviors. Additional features include saga state instance storage (using a saga repository), event correlation, message binding, request and response handling, and event scheduling.

A state machine defines the states, events, and behavior of a finite state machine. Implemented as a class, which is derived from MassTransitStateMachine<T>, a state machine is created once, and then used to apply event triggered behavior to state machine instances.

public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
}

An instance contains the data for a state machine instance. A new instance is created for every consumed initial event where an existing instance with the same CorrelationId was not found. A saga repository is used to persist instances. Instances are classes, and must implement the SagaStateMachineInstance interface.

public class OrderState :
SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
InstanceState(x => x.CurrentState);
}
}

In the example above, the CurrentState property on the saga state machine instance is used to store the instance’s current state. The saga state machine must be configured to use that property which can be one of three types:

TypeDescription
StateThe interface State type. Can be difficult to serialize, typically only used for in-memory instances, but could be used if the repository storage engine supports mapping user types to a storage type.
stringEasy, stores the state name. However, it takes a lot of space as the state name is repeated for every instance.
intSmall, fast, but requires that each possible state be specified, in order, to assign int values to each state.

The CurrentState instance state property is automatically configured if it is a State. For string or int types, the InstanceState method must be used.

For a string instance state, the state name is stored. No additional configuration is required.

public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public State Submitted { get; private set; } = null!;
public State Accepted { get; private set; } = null!;
public OrderStateMachine()
{
InstanceState(x => x.CurrentState);
}
}

An integer can be more efficient to store in a database compared to a verbose string value. In the example below the int property type is used instead. When using an int instance state, the state values must be specified explicitly when calling the InstanceState method.

public class OrderState :
SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public int CurrentState { get; set; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public State Submitted { get; private set; } = null!;
public State Accepted { get; private set; } = null!;
public OrderStateMachine()
{
InstanceState(x => x.CurrentState, Submitted, Accepted);
}
}

This results in the following state values:

StateValue
0None (flows into Initial)
1Initial
2Final
3Submitted
4Accepted