State Machine Requests

Sagas can also send requests, and then react to the responses.

Declaring Requests

public record ValidateOrder(Guid OrderId);
public record OrderValidated(Guid OrderId);

public class OrderStateMachine :
    MassTransitStateMachine<OrderState>
{
    // Generic Types
    // 1. Saga State
    // 2. Request Message
    // 3. Response Message
    public Request<OrderState, ValidateOrder, OrderValidated> ValidateOrder { get; private set; } = null!;
}

Configuring Requests

public class OrderStateMachine :
    MassTransitStateMachine<OrderState>
{
    public Request<OrderState, ValidateOrder, OrderValidated> ValidateOrder { get; private set; } = null!;

    public OrderStateMachine()
    {
        Request(() => ValidateOrder, o =>
        {
            o.Timeout = TimeSpan.FromDays(1);
        });
    }
}

Sending Requests

public class OrderStateMachine :
    MassTransitStateMachine<OrderState>
{
    public Request<OrderState, ValidateOrder, OrderValidated> ValidateOrder { get; private set; } = null!;

    public OrderStateMachine()
    {
        Initially(When(OnRequest)
            .Request(ValidateOrder, sendContext => {
                var saga = sendContext.Saga;
                var msg = sendContext.Message;
                
                return new ValidateOrder();
            })
            .TransitionTo(Requested));

        During(Requested,
            // handle the consumer successfully responding
            When(ValidateOrder.Completed)
                .TransitionTo(Completed),

            // handle the consumer throwing an exception
            When(ValidateOrder.Faulted)
                .TransitionTo(Failed)
        );
    }
}

Request Overrides

.Request(ValidateOrder, sendAddress, context => new ValidateOrder())

.Request(ValidateOrder, _ => sendAddress, context => new ValidateOrder())

.Request(ValidateOrder, sendAddress, async context => 
{
    await Task.Delay(1);
    return new ValidateOrder();
});

.Request(ValidateOrder, _ => sendAddress, async context => 
{
    await Task.Delay(1);
    new ValidateOrder();
})

.Request(ValidateOrder, _ => sendAddress, context => Task.FromResult(new ValidateOrder())

Handling Responses

The defined Request ValidateOrder exposes a few handy properties for use when connecting the saga back to the response.

public class OrderStateMachine :
    MassTransitStateMachine<OrderState>
{
    public Request<OrderState, ValidateOrder, OrderValidated> ValidateOrder { get; private set; } = null!;

    public OrderStateMachine()
    {
        // elided

        During(Requested,
            // handle the consumer successfully responding
            When(ValidateOrder.Completed)
                .TransitionTo(Completed),

            // handle the consumer throwing an exception
            When(ValidateOrder.Faulted)
                .TransitionTo(Failed)
        );
    }
}
Event PropertyComment
CompletedTraps a successful response
Completed2Traps an alternate response type
Completed3Traps an alternate response type
FaultedTraps the Faulted<T> from the consumer throwing an exception
TimeoutExpiredTraps the request timing out
State PropertyComment
PendingA convenience state you can transition to