# Simple Saga Usage ## Introduction 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`. This message starts a new instance of the `OrderSaga` saga as specified by the `IAmStartedByMessages` interface and mapping that [correlates](/nservicebus/sagas/message-correlation.md) `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](/nservicebus/sagas/index.md#ending-a-saga) 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: SolutionDir\Sample\bin\Debug\.sagas\OrderSaga\f5e7ea90-b866-16a0-c911-452f954b95da.json OrderSaga Requesting a CancelOrder that will be executed in 30 seconds. Stop the endpoint now to see the timeout data in the delayed directory SolutionDir\.delayed\20170510054055 ``` ## Endpoint configuration ```cs endpointConfiguration.UsePersistence(); endpointConfiguration.UseTransport(new LearningTransport()); endpointConfiguration.UseSerialization(); ``` ## The Saga ```cs public class OrderSaga : Saga, IAmStartedByMessages, IHandleMessages, IHandleTimeouts { private readonly ILogger logger; public OrderSaga(ILogger logger) { this.logger = logger; } protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.MapSaga(sagaData => sagaData.OrderId) .ToMessage(message => message.OrderId) .ToMessage(message => message.OrderId); } public async Task Handle(StartOrder message, IMessageHandlerContext context) { // Correlation property Data.OrderId is automatically assigned with the value from message.OrderId; logger.LogInformation("StartOrder received with OrderId {OrderId}", message.OrderId); logger.LogInformation("Sending a CompleteOrder that will be delayed by 10 seconds. Stop the endpoint now to see the saga data in: {SagaLocation}", LearningLocationHelper.GetSagaLocation(Data.Id)); var completeOrder = new CompleteOrder { OrderId = Data.OrderId }; var sendOptions = new SendOptions(); sendOptions.DelayDeliveryWith(TimeSpan.FromSeconds(10)); sendOptions.RouteToThisEndpoint(); await context.Send(completeOrder, sendOptions); var timeout = DateTimeOffset.UtcNow.AddSeconds(30); logger.LogInformation($@"Requesting a CancelOrder that will be executed in 30 seconds. Stop the endpoint now to see the timeout data in the delayed directory {LearningLocationHelper.TransportDelayedDirectory(timeout)}"); await RequestTimeout(context, timeout); } public Task Handle(CompleteOrder message, IMessageHandlerContext context) { logger.LogInformation("CompleteOrder received with OrderId {OrderId}", message.OrderId); MarkAsComplete(); return Task.CompletedTask; } public Task Timeout(CancelOrder state, IMessageHandlerContext context) { logger.LogInformation("CompleteOrder not received soon enough OrderId {OrderId}. Calling MarkAsComplete", Data.OrderId); MarkAsComplete(); return Task.CompletedTask; } } ``` ## Location Helper This is a helper class used by the sample to derive the storage locations of the [Learning Transport](/transports/learning/index.md) and the [Learning Persistence](/persistence/learning/index.md). ```cs 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(Guid sagaId) where T : Saga { return Path.Combine(SagaDirectory, typeof(T).Name, $"{sagaId}.json"); } } ```