Prerequisites
This sample requires an instance of SQL Server and a database named Samples.. The database must already exist, as the endpoint creates only the tables it needs.
The Server project connects to localhost,1433 using SQL Server authentication. To run against SQL Server Express instead, use the . connection string shown in the comment directly above the connectionString variable in Program..
Code walk-through
This sample shows a simple client/server scenario.
Clientsends aStartOrdermessage toServer.Serverstarts anOrderSaga.OrderSaga:- Sends a
ShipOrdermessage to itself, and the handler for that message savesOrderShippedbusiness data to the database. - Requests a timeout with
CompleteOrderdata.
- Sends a
- When the
CompleteOrdertimeout fires, theOrderSagapublishes anOrderCompletedevent. Clienthandles theOrderCompletedevent.
NHibernate config
NHibernate is configured with the right driver, dialect, and connection string. Then, since NHibernate needs a way to map the class to the database table, the configuration code does this using the ModelMapper API. Finally, the configuration is passed to the NServiceBus NHibernate persistence.
var endpointConfiguration = new EndpointConfiguration("Samples.NHibernate.Server");
var persistence = endpointConfiguration.UsePersistence<NHibernatePersistence>();
// for SqlExpress use Data Source=.\SqlExpress;Initial Catalog=Samples.NHibernate;Integrated Security=True;Max Pool Size=100;Encrypt=false
var connectionString = @"Server=localhost,1433;Initial Catalog=Samples.NHibernate;User Id=SA;Password=yourStrong(!)Password;Max Pool Size=100;Encrypt=false";
var hibernateConfig = new Configuration();
hibernateConfig.DataBaseIntegration(x =>
{
x.ConnectionString = connectionString;
x.Dialect<MsSql2012Dialect>();
x.Driver<MicrosoftDataSqlClientDriver>();
});
AddMappings(hibernateConfig);
persistence.UseConfiguration(hibernateConfig);
Order saga data
Note that to use NHibernate's lazy-loading feature, all properties on the saga data class must be virtual.
public class OrderSagaData :
ContainSagaData
{
public virtual Guid OrderId { get; set; }
public virtual string OrderDescription { get; set; }
}
Order saga
public class OrderSaga :
Saga<OrderSagaData>,
IAmStartedByMessages<StartOrder>,
IHandleTimeouts<CompleteOrder>
{
static readonly ILog log = LogManager.GetLogger<OrderSaga>();
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderSagaData> mapper)
{
mapper.MapSaga(s => s.OrderId).ToMessage<StartOrder>(m => m.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");
var orderCompleted = new OrderCompleted
{
OrderId = Data.OrderId
};
MarkAsComplete();
return context.Publish(orderCompleted);
}
}
Handler using ISession
The handler uses the ISession instance to store business data.
public class ShipOrderHandler :
IHandleMessages<ShipOrder>
{
public Task Handle(ShipOrder message, IMessageHandlerContext context)
{
var session = context.SynchronizedStorageSession.Session();
var orderShipped = new OrderShipped
{
Id = message.OrderId,
ShippingDate = DateTime.UtcNow,
};
session.Save(orderShipped);
return Task.CompletedTask;
}
}
The database
Data in the database is stored in two different tables.
The saga data
IContainSagaData.maps to theId OrderSagaDataprimary key and unique identifier columnId.IContainSagaData.andOriginator IContainSagaData.map to columns of the same name with typeOriginalMessageId varchar(255).- Custom properties on
OrderSagaData, in this caseOrderDescriptionandOrderId, are also mapped to columns with the same name and the respective types.

The handler stored data
