# Transitioning Saga Correlation IDs This sample illustrates an approach for transitioning between different [correlation IDs](/persistence/sql/saga.md#correlation-ids) in a way that requires no endpoint downtime or migration of saga data stored in SQL. > [!NOTE] > The sample uses three "Phase" endpoint projects to illustrate the iterations of a single endpoint in one solution. ## Prerequisites ### MS SQL Server Ensure an instance of SQL Server (Version 2016 or above for custom saga finders sample, or Version 2012 or above for other samples) is installed and accessible on `localhost` and port `1433`. A Docker image can be used to accomplish this by running `docker run --name mssql -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:latest` in a terminal. Alternatively, change the connection string to point to different SQL Server instance. At startup each endpoint will create its required SQL assets including databases, tables, and schemas. ### MySQL Ensure an instance of MySQL (Version 5.7 or above) is installed and accessible on `localhost` and port `3306`. A Docker image can be used to accomplish this by running `docker run --name mysql -e 'MYSQL_ROOT_PASSWORD=yourStrong(!)Password' -p 3306:3306 -d mysql:latest` in a terminal. Alternatively, change the connection string to point to different MySQL instance. At startup each endpoint will create the required SQL assets including databases, tables, and schemas. ### Oracle Ensure an instance of Oracle (Version 11g or later) is installed and accessible on `localhost` and port `1521`. A Docker image can be used to accomplish this by running `docker run --name oracle -e 'ORACLE_PASSWORD=yourStrong(!)Password' -p 1521:1521 -d gvenzl/oracle-free:23-slim` in a terminal. Alternatively, change the connection string to point to different Oracle instance. At startup each endpoint will create the required SQL assets including databases, tables, and schemas. ### PostgreSQL Ensure an instance of PostgreSQL (Version 10 or later) is installed and accessible on `localhost` and port `5432`. A Docker image can be used to accomplish this by running `docker run --name postgres -e 'POSTGRES_PASSWORD=yourStrong(!)Password' -p 5432:5432 -d postgres:latest` in a terminal. Alternatively, change the connection string to point to different PostgreSQL instance. At startup each endpoint will create the required SQL assets including databases, tables, and schemas. ## Scenario The sample uses a hypothetical "Order" scenario where the requirement is to transition from an integer correlation ID `OrderNumber` to a GUID correlation ID `OrderId`. To move between phases, after running each phase, adjust the startup project list in the solution properties. E.g., after phase 1, disable endpoints that contain "Phase1" in the name, and enable endpoints that contain "Phase2". ## Phases ### Phase 1 In the initial phase, an integer `OrderNumber` is used for the correlation ID. In the `ConfigureHowToFindSaga` method, the saga maps `StartOrder.OrderNumber` from the incoming message to `OrderSagaData.OrderNumber` in the saga data. #### Message ```cs public class StartOrder : IMessage { public int OrderNumber { get; set; } } ``` #### Saga ```cs public class OrderSaga : Saga, IAmStartedByMessages { readonly static ILog log = LogManager.GetLogger(); protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.MapSaga(saga => saga.OrderNumber) .ToMessage(msg => msg.OrderNumber); } public Task Handle(StartOrder message, IMessageHandlerContext context) { Data.OrderNumber = message.OrderNumber; log.Info($"Received StartOrder message. Data.OrderNumber={Data.OrderNumber}"); return Task.CompletedTask; } } ``` #### SagaData ```cs public class OrderSagaData : ContainSagaData { public int OrderNumber { get; set; } } ``` ### Phase 2 In the second phase, a GUID `OrderId` is added. The saga still maps `StartOrder.OrderNumber` to `OrderSagaData.OrderNumber` in the `ConfigureHowToFindSaga` method. However, it also introduces a correlation to `OrderSagaData.OrderId` via a `transitionalCorrelationProperty` in the `[SqlSaga]` attribute. #### Message ```cs public class StartOrder : IMessage { public int OrderNumber { get; set; } public Guid OrderId { get; set; } } ``` #### Saga ```cs [SqlSaga(transitionalCorrelationProperty: nameof(OrderSagaData.OrderId))] public class OrderSaga : Saga, IAmStartedByMessages { readonly static ILog log = LogManager.GetLogger(); protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.MapSaga(saga => saga.OrderNumber) .ToMessage(msg => msg.OrderNumber); } public Task Handle(StartOrder message, IMessageHandlerContext context) { Data.OrderId = message.OrderId; Data.OrderNumber = message.OrderNumber; log.Info($"Received StartOrder message. OrderId={Data.OrderId}. OrderNumber={Data.OrderNumber}"); return Task.CompletedTask; } } ``` #### SagaData ```cs public class OrderSagaData : ContainSagaData { public int OrderNumber { get; set; } public Guid OrderId { get; set; } } ``` > [!WARNING] > Prior to moving to Phase 3, it is necessary to verify that all existing sagas have the `Correlation_OrderId` column populated. This can either be inferred by the business knowledge (i.e., certain saga may have a known and constrained lifetime) or by querying the database. ### Phase 3 In the third phase, the integer `OrderNumber` is removed, leaving only the `OrderId`. The saga now maps `StartOrder.OrderId` to `OrderSagaData.OrderId` in the `ConfigureHowToFindSaga` method. #### Message ```cs public class StartOrder : IMessage { public Guid OrderId { get; set; } } ``` #### Saga ```cs public class OrderSaga : Saga, IAmStartedByMessages { readonly 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) { Data.OrderId = message.OrderId; log.Info($"Received StartOrder message. OrderId={Data.OrderId}."); return Task.CompletedTask; } } ``` #### SagaData ```cs public class OrderSagaData : ContainSagaData { public Guid OrderId { get; set; } } ```