﻿# Simple NHibernate Persistence Usage


## Prerequisites

This sample requires an instance of SQL Server and a database named `Samples.NHibernate`. 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 `.\SqlExpress` connection string shown in the comment directly above the `connectionString` variable in `Program.cs`.

## Code walk-through

This sample shows a simple client/server scenario.

* `Client` sends a `StartOrder` message to `Server`.
* `Server` starts an `OrderSaga`.
* `OrderSaga`:
  * Sends a `ShipOrder` message to itself, and the handler for that message saves `OrderShipped` business data to the database.
  * Requests a timeout with `CompleteOrder` data.
* When the `CompleteOrder` timeout fires, the `OrderSaga` publishes an `OrderCompleted` event.
* `Client` handles the `OrderCompleted` event.

### 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.

<!-- snippet: config -->

```cs
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);
```

<!-- endsnippet -->

### Order saga data

Note that to use NHibernate's lazy-loading feature, all properties on the saga data class must be `virtual`.

<!-- snippet: sagadata -->

```cs
public class OrderSagaData :
    ContainSagaData
{
    public virtual Guid OrderId { get; set; }
    public virtual string OrderDescription { get; set; }
}
```

<!-- endsnippet -->

### Order saga

<!-- snippet: ordersaga -->

```cs
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);
    }
}
```

<!-- endsnippet -->

### Handler using ISession

The handler uses the `ISession` instance to store business data.

<!-- snippet: handler -->

```cs
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;
    }
}
```

<!-- endsnippet -->

## The database

Data in the database is stored in two 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 `OrderSagaData`, in this case `OrderDescription` and `OrderId`, are also mapped to columns with the same name and the respective types.

![Query results for the OrderSagaData table](sagadata.png)

### The handler stored data

![Query results for the OrderShipped table](handlerdoc.png)
