# NHibernate Custom Saga Finding Logic ## Code walk-through When the default Saga message mappings do not satisfy the requirements, custom logic can be put in place to allow NServiceBus to find a saga data instance based on logic that best suits the environment. ## Prerequisites 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. This sample creates a database named `NsbSamplesNhCustomSagaFinder`. ### NHibernate setup This sample uses the [NHibernate persistence](/persistence/nhibernate/index.md), configured as follows: ```cs // for SqlExpress use Data Source=.\SqlExpress;Initial Catalog=NsbSamplesNhCustomSagaFinder;Integrated Security=True;Max Pool Size=100;Encrypt=false var connectionString = @"Server=localhost,1433;Initial Catalog=NsbSamplesNhCustomSagaFinder;User Id=SA;Password=yourStrong(!)Password;Max Pool Size=100;Encrypt=false"; var hibernateConfig = new Configuration(); hibernateConfig.DataBaseIntegration(x => { x.ConnectionString = connectionString; x.Dialect(); x.Driver(); }); var persistence = endpointConfiguration.UsePersistence() .UseConfiguration(hibernateConfig); ``` ## The Saga This sample contains a very simple order management saga with these responsibilities: - Handling the creation of an order. - Offloading the payment process to a another handler. - Handling the completion of the payment process. - Completing the order. ```cs using Microsoft.Extensions.Logging; public class OrderSaga(ILogger logger) : Saga, IAmStartedByMessages, IHandleMessages, IHandleMessages { protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.MapSaga(saga => saga.OrderId) .ToMessage(msg => msg.OrderId) .ToMessage(msg => msg.OrderId); mapper.ConfigureFinderMapping(); } public Task Handle(StartOrder message, IMessageHandlerContext context) { Data.PaymentTransactionId = Guid.NewGuid().ToString(); logger.LogInformation("Saga with OrderId {SagaOrderId} received StartOrder with OrderId {MessageOrderId}", Data.OrderId, message.OrderId); var issuePaymentRequest = new IssuePaymentRequest { PaymentTransactionId = Data.PaymentTransactionId }; return context.SendLocal(issuePaymentRequest); } public Task Handle(CompletePaymentTransaction message, IMessageHandlerContext context) { logger.LogInformation("Transaction with Id {PaymentTransactionId} completed for order id {OrderId}", Data.PaymentTransactionId, Data.OrderId); var completeOrder = new CompleteOrder { OrderId = Data.OrderId }; return context.SendLocal(completeOrder); } public Task Handle(CompleteOrder message, IMessageHandlerContext context) { logger.LogInformation("Saga with OrderId {SagaOrderId} received CompleteOrder with OrderId {MessageOrderId}", Data.OrderId, message.OrderId); MarkAsComplete(); return Task.CompletedTask; } } ``` It is important to note that the saga is not sending the order ID to the payment processor. Instead, it is sending a payment transaction ID. This saga needs to be correlated by more than one property. For example, `OrderId` and `PaymentTransactionId`. This requires both of these properties to be treated as unique. ```cs class CompletePaymentTransactionSagaFinder : ISagaFinder { public Task FindBy(CompletePaymentTransaction message, ISynchronizedStorageSession storageSession, IReadOnlyContextBag context, CancellationToken cancellationToken = default) { var session = storageSession.Session(); var orderSagaData = session.QueryOver() .Where(d => d.PaymentTransactionId == message.PaymentTransactionId) .SingleOrDefault(); return Task.FromResult(orderSagaData); } } ``` A saga finder is a class that implements `IFindSagas.Using`. The class will be detected automatically by NServiceBus at endpoint configuration time. The `FindBy` method will be invoked by NServiceBus each time a message of type `TMessage` is received by a saga which stores its state using an instance of the `TSagaData` type.