# Simple Azure Table Persistence Usage This sample demonstrates a client/server scenario that uses Azure Table Persistence to store sagas. ## Prerequisites Ensure that an instance of the latest [Azurite Emulator](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite) or [Azure Cosmos DB Emulator](https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator) is running. ## Projects ### SharedMessages * The shared message contracts used by all endpoints. ### Client * Sends the `StartOrder` message to `Server`. * Receives and handles the `OrderCompleted` event. ### Server * Receives the `StartOrder` message and starts an `OrderSaga`. * `OrderSaga` sends a `ShipOrder` message to itself and requests a timeout with an instance of `CompleteOrder` with the saga data. * `ShipOrderHandler` handles the `ShipOrder` message. * `OrderSaga` publishes an `OrderCompleted` event when the `CompleteOrder` timeout fires. ## Persistence config Configure the endpoint to use Azure Table Persistence. ```cs var useStorageTable = true; var endpointConfiguration = new EndpointConfiguration("Samples.AzureTable.Simple.Server"); var persistence = endpointConfiguration.UsePersistence(); var connection = useStorageTable ? "UseDevelopmentStorage=true" : "TableEndpoint=https://localhost:8081/;AccountName=AzureTableSamples;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; var account = CloudStorageAccount.Parse(connection); persistence.UseCloudTableClient(account.CreateCloudTableClient()); ``` In the non-transactional mode the saga ID is used as a partition key. ## Order saga data ```cs public class OrderSagaData : ContainSagaData { public Guid OrderId { get; set; } public string OrderDescription { get; set; } } ``` ## Order saga ```cs public class OrderSaga : Saga, IAmStartedByMessages, IHandleTimeouts { static ILog log = LogManager.GetLogger(); protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.MapSaga(saga => saga.OrderId).ToMessage(msg => msg.OrderId); } public Task Handle(StartOrder message, IMessageHandlerContext context) { var orderDescription = $"The saga for order {message.OrderId}"; Data.OrderDescription = orderDescription; log.Info($"Received StartOrder message {Data.OrderId}. Starting Saga"); var shipOrder = new ShipOrder { OrderId = message.OrderId }; log.Info("Order will complete in 5 seconds"); var timeoutData = new CompleteOrder { OrderDescription = orderDescription, }; return Task.WhenAll( context.SendLocal(shipOrder), RequestTimeout(context, TimeSpan.FromSeconds(5), timeoutData) ); } public Task Timeout(CompleteOrder state, IMessageHandlerContext context) { log.Info($"Saga with OrderId {Data.OrderId} completed"); MarkAsComplete(); var orderCompleted = new OrderCompleted { OrderId = Data.OrderId }; return context.Publish(orderCompleted); } } ```