Skip to content

State Machine Scheduled Events

A state machine can schedule events, which uses the message scheduler to schedule a message for delivery to the instance. First, the schedule must be declared.

public interface OrderCompletionTimeoutExpired
{
Guid OrderId { get; }
}
public class OrderState :
SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
public Guid? OrderCompletionTimeoutTokenId { get; set; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
Schedule(() => OrderCompletionTimeout, instance => instance.OrderCompletionTimeoutTokenId, s =>
{
s.Delay = TimeSpan.FromDays(30);
s.Received = r => r.CorrelateById(context => context.Message.OrderId);
});
}
public Schedule<OrderState, OrderCompletionTimeoutExpired> OrderCompletionTimeout { get; private set; }
}

The configuration specifies the Delay, which can be overridden by the schedule activity, and the correlation expression for the Received event. The state machine can consume the Received event as shown. The OrderCompletionTimeoutTokenId is a Guid? instance property used to keep track of the scheduled message tokenId which can later be used to unschedule the event.

public interface OrderCompleted
{
Guid OrderId { get; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
During(Accepted,
When(OrderCompletionTimeout.Received)
.PublishAsync(context => context.Init<OrderCompleted>(new { OrderId = context.Saga.CorrelationId }))
.Finalize());
}
public Schedule<OrderState, OrderCompletionTimeoutExpired> OrderCompletionTimeout { get; private set; }
}

The event can be scheduled using the Schedule activity.

public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
During(Submitted,
When(OrderAccepted)
.Schedule(OrderCompletionTimeout, context => context.Init<OrderCompletionTimeoutExpired>(new { OrderId = context.Saga.CorrelationId }))
.TransitionTo(Accepted));
}
}

As stated above, the delay can be overridden by the Schedule activity. Both instance and message (context.Data) content can be used to calculate the delay.

public interface OrderAccepted
{
Guid OrderId { get; }
TimeSpan CompletionTime { get; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
During(Submitted,
When(OrderAccepted)
.Schedule(OrderCompletionTimeout, context => context.Init<OrderCompletionTimeoutExpired>(new { OrderId = context.Saga.CorrelationId }),
context => context.Message.CompletionTime)
.TransitionTo(Accepted));
}
}

Once the scheduled event is received, the OrderCompletionTimeoutTokenId property is cleared.

If the scheduled event is no longer needed, the Unschedule activity can be used.

public interface OrderAccepted
{
Guid OrderId { get; }
TimeSpan CompletionTime { get; }
}
public class OrderStateMachine :
MassTransitStateMachine<OrderState>
{
public OrderStateMachine()
{
DuringAny(
When(OrderCancellationRequested)
.RespondAsync(context => context.Init<OrderCanceled>(new { OrderId = context.Saga.CorrelationId }))
.Unschedule(OrderCompletionTimeout)
.TransitionTo(Canceled));
}
}