Simple NHibernate Persistence Usage

Component: NHibernate Persistence
NuGet Package NServiceBus.NHibernate (8.x)
Target NServiceBus Version: 7.x


The sample relies on .\SqlExpress and the existence of a database named Samples.NHibernate.

Code walk-through

This sample shows a simple client/server scenario.

  • Client sends a StartOrder message to Server.
  • Server starts an OrderSaga.
  • OrderSaga requests a timeout with CompleteOrder data.
  • When the CompleteOrder timeout fires, the OrderSaga publishes an OrderCompleted event.
  • Server then publishes a message that the client has subscribed to.
  • Client handles the OrderCompleted event.

NHibernate config

NHibernate is first 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 used to run the endpoint.

var endpointConfiguration = new EndpointConfiguration("Samples.NHibernate.Server");
var persistence = endpointConfiguration.UsePersistence<NHibernatePersistence>();

var nhConfig = new Configuration();
nhConfig.SetProperty(Environment.ConnectionProvider, "NHibernate.Connection.DriverConnectionProvider");
nhConfig.SetProperty(Environment.ConnectionDriver, "NHibernate.Driver.Sql2008ClientDriver");
nhConfig.SetProperty(Environment.Dialect, "NHibernate.Dialect.MsSql2008Dialect");
nhConfig.SetProperty(Environment.ConnectionStringName, "NServiceBus/Persistence");



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 :
    public virtual Guid OrderId { get; set; }
    public virtual string OrderDescription { get; set; }

Order saga

public class OrderSaga :
    static ILog log = LogManager.GetLogger<OrderSaga>();

    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderSagaData> mapper)
        mapper.ConfigureMapping<StartOrder>(message => message.OrderId)
            .ToSaga(sagaData => sagaData.OrderId);

    public Task Handle(StartOrder message, IMessageHandlerContext context)
        Data.OrderId = message.OrderId;
        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(
            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
        return context.Publish(orderCompleted);

Handler using ISession

The handler uses the ISession instance to store business data.

public class ShipOrderHandler :
    public Task Handle(ShipOrder message, IMessageHandlerContext context)
        var session = context.SynchronizedStorageSession.Session();
        var orderShipped = new OrderShipped
            Id = message.OrderId,
            ShippingDate = DateTime.UtcNow,


        return Task.CompletedTask;

The database

Data in the database is stored in three different tables.

The saga data

  • IContainSagaData.Id maps to the OrderSagaData primary key and unique identifier column Id.
  • IContainSagaData.Originator and IContainSagaData.OriginalMessageId map to columns of the same name with type varchar(255).
  • Custom properties on SagaData, in this case OrderDescription and OrderId, are also mapped to columns with the same name and the respecting types.

The timeouts

  • The subscriber is stored in the Destination column and includes Queue and Machine information.
  • The endpoint that initiated the timeout is stored in the Endpoint column.
  • The connected saga ID is stored in a SagaId column.
  • The serialized data for the message is stored in a State column.
  • The scheduled timestamp for the timeout is stored in a Time column.
  • Any headers associated with the timeout are stored in an array of key value pairs stored in the 'Headers' column.

The subscriptions

Note that the message type maps to multiple subscriber endpoints.

  • The Subscription message type and version are stored in the MessageType column.
  • The list of subscribers is stored in an array of objects each containing Queue and MachineName information.

The handler stored data

Related Articles

  • Persistence
    Features of NServiceBus requiring persistence include timeouts, sagas, and subscription storage.
  • Sagas
    NServiceBus uses event-driven architecture to include fault-tolerance and scalability in long-term business processes.

Last modified