Simple Saga Usage

Component: NServiceBus
NuGet Package: NServiceBus (8-pre)
This page targets a pre-release version. Pre-releases are subject to change and samples are not guaranteed to be fully functional.


This sample shows a simple saga to manage an order.

Once the project is started, press Enter to send a StartOrder message with a randomly assigned OrderId that starts a new saga instance. This is a result of as configuration specified with the IAmStartedByMessages interface and mapping that correlates StartOrder.OrderId property on the message with OrderSagaData.OrderId property on the saga data.

When processing the StartOrder message each saga instance requests a 30-second CancelOrder timeout that will mark the saga as complete if the saga is not already complete.

The output to the console will be

Storage locations:
Learning Persister: SolutionDir\Sample\bin\Debug\.sagas
Learning Transport: SolutionDir\

Press 'Enter' to send a StartOrder message
Press any other key to exit

Sent StartOrder with OrderId 8d80b684-cc77-4ec6-867b-090bc38d914c.

OrderSaga StartOrder received with OrderId 8d80b684-cc77-4ec6-867b-090bc38d914c
OrderSaga Sending a CompleteOrder that will be delayed by 10 seconds
Stop the endpoint now to see the saga data in:
OrderSaga Requesting a CancelOrder that will be executed in 30 seconds.
Stop the endpoint now to see the timeout data in the delayed directory

Endpoint configuration

endpointConfiguration.UseTransport(new LearningTransport());

The Saga

public class OrderSaga :
    static ILog log = LogManager.GetLogger<OrderSaga>();

    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderSagaData> mapper)
        mapper.MapSaga(sagaData => sagaData.OrderId)
            .ToMessage<StartOrder>(message => message.OrderId)
            .ToMessage<CompleteOrder>(message => message.OrderId);

    public async Task Handle(StartOrder message, IMessageHandlerContext context)
        // Correlation property Data.OrderId is automatically assigned with the value from message.OrderId;
        log.Info($"StartOrder received with OrderId {message.OrderId}");

        log.Info($@"Sending a CompleteOrder that will be delayed by 10 seconds
Stop the endpoint now to see the saga data in:
        var completeOrder = new CompleteOrder
            OrderId = Data.OrderId
        var sendOptions = new SendOptions();
        await context.Send(completeOrder, sendOptions)

        var timeout = DateTimeOffset.UtcNow.AddSeconds(30);
        log.Info($@"Requesting a CancelOrder that will be executed in 30 seconds.
Stop the endpoint now to see the timeout data in the delayed directory
        await RequestTimeout<CancelOrder>(context, timeout)

    public Task Handle(CompleteOrder message, IMessageHandlerContext context)
        log.Info($"CompleteOrder received with OrderId {message.OrderId}");
        return Task.CompletedTask;

    public Task Timeout(CancelOrder state, IMessageHandlerContext context)
        log.Info($"CompleteOrder not received soon enough OrderId {Data.OrderId}. Calling MarkAsComplete");
        return Task.CompletedTask;

Location Helper

This is a helper class used by the sample to derive the storage locations of the Learning Transport and the Learning Persistence.

public static class LearningLocationHelper
    public static string TransportDirectory;
    public static string SagaDirectory;

    static LearningLocationHelper()
        var location = Assembly.GetExecutingAssembly().Location;
        var runningDirectory = Directory.GetParent(location).FullName;
        SagaDirectory = Path.Combine(runningDirectory, ".sagas");
        TransportDirectory = Path.GetFullPath(Path.Combine(runningDirectory, @"..\..\..\"));

    public static string TransportDelayedDirectory(DateTimeOffset dateTime)
        return Path.Combine(TransportDirectory, ".delayed", dateTime.UtcDateTime.ToString("yyyyMMddHHmmss"));

    public static string GetSagaLocation<T>(Guid sagaId)
        where T : Saga
        return Path.Combine(SagaDirectory, typeof(T).Name, $"{sagaId}.json");

Related Articles

Last modified