﻿# Simple RavenDB Persistence Usage

<!-- Version variant: raven_10; default: [/samples/ravendb/simple/index.md](/samples/ravendb/simple/index.md) -->


## Code walk-through

This sample shows a simple Client + Server scenario.

 1. `Client` sends a `StartOrder` message to `Server`.
 2. `Server` starts an `OrderSaga`.
 3. `OrderSaga` requests a timeout with a `CompleteOrder` data.
 4. When the `CompleteOrder` timeout fires the `OrderSaga` publishes a `OrderCompleted` event.
 5. The Server then publishes a message that the client subscribes to.
 6. `Client` handles `OrderCompleted` event.


### Raven Config

1. In the RavenDB studio, create a database named `RavenSimpleSample`. RavenDB does not create the database automatically.
2. Configure the endpoint to use RavenDB persistence. The URL in the `Urls` collection may need to be changed to match the database host/port in use.


<!-- snippet: config -->

```cs
var endpointConfiguration = new EndpointConfiguration("Samples.RavenDB.Server");
using var documentStore = new DocumentStore
{
    Urls = ["http://localhost:8080"],
    Database = "RavenSimpleSample",
};

documentStore.Initialize();

var persistence = endpointConfiguration.UsePersistence<RavenDBPersistence>();
persistence.SetDefaultDocumentStore(documentStore);
```

<!-- endsnippet -->

> [!NOTE]
> When the RavenDB `DocumentStore` is created by the user at endpoint configuration time, it's important to dispose it by calling the `Dispose()` method, before shutting down the endpoint process.



### Order Saga Data

<!-- snippet: sagadata -->

```cs
public class OrderSagaData :
    ContainSagaData
{
    public Guid OrderId { get; set; }
    public string OrderDescription { get; set; }
}
```

<!-- endsnippet -->


### Order Saga

<!-- snippet: thesaga -->

```cs
public class OrderSaga(ILogger<OrderSaga> logger) :
    Saga<OrderSagaData>,
    IAmStartedByMessages<StartOrder>,
    IHandleTimeouts<CompleteOrder>
{
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderSagaData> mapper)
    {
        mapper.MapSaga(sagaData => sagaData.OrderId)
            .ToMessage<StartOrder>(message => message.OrderId);
    }

    public Task Handle(StartOrder message, IMessageHandlerContext context)
    {
        var orderDescription = $"The saga for order {message.OrderId}";
        Data.OrderDescription = orderDescription;

        logger.LogInformation("Received StartOrder message {OrderId}. Starting Saga", Data.OrderId);

        var shipOrder = new ShipOrder
        {
            OrderId = message.OrderId
        };

        logger.LogInformation("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)
    {
        logger.LogInformation("Saga with OrderId {OrderId} completed", Data.OrderId);
        var orderCompleted = new OrderCompleted
        {
            OrderId = Data.OrderId
        };
        MarkAsComplete();
        return context.Publish(orderCompleted);
    }
}
```

<!-- endsnippet -->


### Handler Using RavenDB Session

The handler accesses the same RavenDB `ISession` via `ISessionProvider`.

<!-- snippet: handler -->

```cs
public class ShipOrderHandler :
    IHandleMessages<ShipOrder>
{
    public Task Handle(ShipOrder message, IMessageHandlerContext context)
    {
        var session = context.SynchronizedStorageSession.RavenSession();
        var orderShipped = new OrderShipped
        {
            OrderId = message.OrderId,
            ShippingDate = DateTime.UtcNow,
        };
        return session.StoreAsync(orderShipped, context.CancellationToken);
    }
}
```

<!-- endsnippet -->


## The Data in RavenDB

The data in RavenDB is stored in three different collections.

> [!WARNING]
> By default, this sample uses the [Learning Transport](/transports/learning/index.md), which has built-in support for timeouts and subscriptions. To see the data for timeouts and subscriptions, it's necessary to change the sample to a different transport that does not have these native features such as [MSMQ](/transports/msmq/index.md).


### The Saga Data

 * `IContainSagaData.Id` maps to the native RavenDB document `Id`.
 * `IContainSagaData.Originator` and `IContainSagaData.OriginalMessageId` map to simple properties.
 * Custom properties on the SagaData, in this case `OrderDescription` and `OrderId`, are also mapped to simple properties.

```json
{
    "IdentityDocId": "OrderSagaData/OrderId/33e54adb-10fe-ac05-abdd-a656e8f995b3",
    "Data": {
        "$type": "OrderSagaData, Server",
        "OrderId": "683e7b20-527a-475d-847c-79ef6b0f40a1",
        "OrderDescription": "The saga for order 683e7b20-527a-475d-847c-79ef6b0f40a1",
        "Id": "9bf646a0-25b1-4a79-afe3-adf600965476",
        "Originator": "Samples.RavenDB.Client",
        "OriginalMessageId": "06cdd874-de9b-40bb-8b90-adf600965448"
    },
    "@metadata": {
        "@collection": "SagaDataContainers",
        "Raven-Clr-Type": "NServiceBus.Persistence.RavenDB.SagaDataContainer, NServiceBus.RavenDB",
        "NServiceBus-Persistence-RavenDB-SagaDataContainer-SchemaVersion": "1.0.0"
    }
}
```

### The Handler Stored data

```json
{
    "OrderId": "683e7b20-527a-475d-847c-79ef6b0f40a1",
    "ShippingDate": "2021-12-06T09:07:20.1902988Z",
    "@metadata": {
        "@collection": "OrderShippeds",
        "Raven-Clr-Type": "OrderShipped, Server"
    }
}
```
