Integrating RavenDB with .NET Aspire project for enhanced development

by Shiran Shalom Abramowsky

Introduction

Recently, RavenDB was integrated with .NET Aspire. It helps you manage all your application dependencies, including your database, to enhance dev-time orchestration.

.NET Aspire manages well-defined app components, runs them in containers, provides pre-configured observability, and much more. In this guide, since RavenDB is now Aspire-ready, we’ll show you how to leverage it to abstract out dependencies management. 

We’ll walk you through:

  • Both hosting and client integrations – We’ll set up a .NET Aspire project, deploy a RavenDB server, and create an API service that interacts with the database.
  • We’ll implement a fun use case inspired by the TV show Friends to demonstrate RavenDB’s capabilities, including time series, counters, and queries.

So grab a cup of coffee ☕, and let’s get started!


Setting Up RavenDB with .NET Aspire

0. Requirements 

  • .NET 8.0+
  • Container runtime like Docker Desktop or Podman
  • Visual Studio, version 17.9 or higher
  • Alternatively, Rider with the .NET Aspire plugin

1. Creating an Empty .NET Aspire Application from Visual Studio

Let’s start with something simple: creating an empty .NET Aspire application in Visual Studio.

  1. Open Visual Studio and go to File > New > Project.
  2. Search for and select .NET Aspire Application.
  3. Choose a name for your project (e.g., RavenDBAspireExample) and click Create.
  4. The solution will contain multiple projects, including:
    • AppHost (Manages hosting and dependencies)
    • ServiceDefaults (Provides common service configurations)

2. Creating the API Service Project

Now, we may proceed with adding RavenDB integration.

  1. Right-click on the `RavenDBAspireExample` solution in Solution Explorer.
  2. Select Add > New Project.
  3. Choose ASP.NET Core Web API.
  4. Name the project `RavenDBAspireExample.ApiService`.
  5. Click Create.
  6. Ensure the project is added to the solution and appears under the solution structure.

3. Connecting the API Service to ServiceDefaults

  1. Right-click on the APIService project in Solution Explorer.
  2. Select Add > Project Reference…
  3. Choose the ServiceDefaults project from the list and click OK.

4. Adding RavenDB Hosting Integration and Referencing API Service in AppHost

Now, before connecting AppHost to APIService, we install the 📦 CommunityToolkit.Aspire.Hosting.RavenDB. We can do that in two ways. Add it using the command using NuGet. You can find more information here. You can use the following command to do that:

dotnet add package CommunityToolkit.Aspire.Hosting.RavenDB

Alternatively, you can install it via Visual Studio:

  1. Right-click on the AppHost project.
  2. Select Manage NuGet Packages.
  3. Search for `CommunityToolkit.Aspire.Hosting.RavenDB` and install it.

Make sure that after installation, the package exists under your AppHost > Dependencies > Packages:

Adding a Reference to the API Service in AppHost

We just need to add a reference to enable the AppHost project to work with the APIService project. 

  1. Right-click on the AppHost project in Solution Explorer.
  2. Select Add > Project Reference….
  3. Choose the APIService project from the list and click OK.

Next, modify Program.cs in AppHost to register the API Service project:

using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var serverResource = builder.AddRavenDB(name: "ravenServerResource");
var databaseResource = serverResource.AddDatabase(name: "ravenDatabaseResource", databaseName: "myDatabase");

builder.AddProject<RavenDBAspireExample_ApiService>("RavenApiService")
    .WithReference(databaseResource)
    .WaitFor(databaseResource);

builder.Build().Run();

5. Previewing the .NET Aspire Dashboard

Once the AppHost project is up and running, we can explore the .NET Aspire Dashboard, which allows us to monitor our application’s resources.

  1. Run the AppHost project.
  2. Look for the Login to Dashboard URL in the console output.
  3. Copy and paste the URL into your browser.

The dashboard provides:

  • An overview of all running services in the Aspire application.
  • Logs and metrics for debugging and monitoring.
  • Access to RavenDB Studio, where you can explore the database.

Using the dashboard, you can verify that the RavenDB server is running and connected to the APIService project.

6. Adding RavenDB Client Integration

With the RavenDB client integration, your application can easily connect to a RavenDB instance and engage with its databases through the `IDocumentStore`. Let’s do it now.

Installing the Client Package

Like before, In the APIService project, install the 📦 CommunityToolkit.Aspire.RavenDB.Client NuGet package in one of two ways.

dotnet add package CommunityToolkit.Aspire.RavenDB.Client

Or via Visual Studio:

  1. Right-click on the APIService project.
  2. Select Manage NuGet Packages.
  3. Search for `CommunityToolkit.Aspire.RavenDB.Client` and install it.

Configuring the RavenDB Client in APIService

And then let’s modify the Program.cs file of the APIService project:

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddRavenDBClient(connectionName: "ravenDatabaseResource", configureSettings: settings =>
{
    settings.CreateDatabase = true;
    settings.DatabaseName = "myDatabase";
});
var app = builder.Build();

// here we’ll add some API endpoints shortly…

app.Run();

* Ensure the `connectionName` matches the one used in the AppHost project.


Building the API: Central Perk Coffee Tracker

Great! Now that we’ve got our RavenDB server and client all set up, let’s dive in and create a fun API service to track how much coffee each of our favorite Friends characters drinks. Sounds like a fun project, doesn’t it? This example demonstrates how to:

  • Store documents (Character profiles)
  • Use time series (Track coffee consumption over time)
  • Use counters (Keep a tally of total coffee cups per character)
  • Query data (Retrieve coffee consumption statistics)

You can also check the repo here.

1. Adding Character Profiles

First, create the code to store Friends characters in the database.

app.MapPut("/characters", async (IDocumentStore documentStore) =>
{
    var users = new Character[]
    {
        new() {Id = "characters/joey", Name = "Joey Tribbiani", FavoriteDrink = "Espresso"},
        new() {Id = "characters/chandler", Name = "Chandler Bing", FavoriteDrink = "Black Coffee"},
        new() {Id = "characters/ross", Name = "Ross Geller", FavoriteDrink = "Macchiato"},
        new() {Id = "characters/monica", Name = "Monica Geller", FavoriteDrink = "Latte"},
        new() {Id = "characters/phoebe", Name = "Phoebe Buffay-Hannigan", FavoriteDrink = "Herbal Tea"},
        new() {Id = "characters/rachel", Name = "Rachel Green", FavoriteDrink = "Cappuccino"}
    };

    using var session = documentStore.OpenAsyncSession();

    foreach (var user in users)
        await session.StoreAsync(user);

    await session.SaveChangesAsync();
});

2. Logging Coffee Consumption

Now, let’s record a coffee drink event in a time series and increment a counter.

app.MapPost("/characters/coffee", async (IDocumentStore documentStore, [FromQuery] string id) =>
{
    var now = DateTime.UtcNow;

    using var session = documentStore.OpenAsyncSession();

    session.TimeSeriesFor(id, "coffee-drinks").Append(now, 1);
    session.CountersFor(id).Increment("TotalCups", 1);
    await session.SaveChangesAsync();

    return Results.Ok($"Logged coffee drink for {id} at {now}");
});

3. Fetching Coffee Stats

This code retrieves coffee-drinking trends for a character over time.

app.MapGet("/characters/coffee-stats", async (IDocumentStore documentStore, [FromQuery] string id) =>
{
    using var session = documentStore.OpenAsyncSession();

    var coffeeData = await session.TimeSeriesFor(id, "coffee-drinks")
        .GetAsync(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow);
    var totalCups = await session.CountersFor(id).GetAsync("TotalCups");

    return Results.Ok(new { coffeeData, totalCups });
});

4. Finding the Top Coffee Drinker

Finally, this piece of code returns to us the character who consumed the most coffee.

app.MapGet("/top-coffee-drinker", (IDocumentStore documentStore) =>
{
    using var session = documentStore.OpenSession();

    var result = session.Advanced.RawQuery<CounterResult>
            ("from characters as c where c.\"@metadata\".\"@counters\" == \"TotalCups\" " +
             "select { Name: c.Name, TotalCups: counter(c, \"TotalCups\") }")
            .ToList()
            .OrderByDescending(x => x.TotalCups)
            .FirstOrDefault();

    return Results.Ok($"The TOP coffee drinker is '{result?.Name}' with {result?.TotalCups} Total Cups!");
});

And here is the full Program.cs:

using Microsoft.AspNetCore.Mvc;
using Raven.Client.Documents;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddRavenDBClient(connectionName: "ravenDatabaseResource", configureSettings: settings =>
{
    settings.CreateDatabase = true;
    settings.DatabaseName = "myDatabase";
});
var app = builder.Build();

app.MapPut("/characters", async (IDocumentStore documentStore) =>
{
    var users = new Character[]
    {
        new() {Id = "characters/joey", Name = "Joey Tribbiani", FavoriteDrink = "Espresso"},
        new() {Id = "characters/chandler", Name = "Chandler Bing", FavoriteDrink = "Black Coffee"},
        new() {Id = "characters/ross", Name = "Ross Geller", FavoriteDrink = "Macchiato"},
        new() {Id = "characters/monica", Name = "Monica Geller", FavoriteDrink = "Latte"},
        new() {Id = "characters/phoebe", Name = "Phoebe Buffay-Hannigan", FavoriteDrink = "Herbal Tea"},
        new() {Id = "characters/rachel", Name = "Rachel Green", FavoriteDrink = "Cappuccino"}
    };

    using var session = documentStore.OpenAsyncSession();

    foreach (var user in users)
        await session.StoreAsync(user);

    await session.SaveChangesAsync();
});

app.MapPost("/characters/coffee", async (IDocumentStore documentStore, [FromQuery] string id) =>
{
    var now = DateTime.UtcNow;

    using var session = documentStore.OpenAsyncSession();

    session.TimeSeriesFor(id, "coffee-drinks").Append(now, 1);
    session.CountersFor(id).Increment("TotalCups", 1);
    await session.SaveChangesAsync();

    return Results.Ok($"Logged coffee drink for {id} at {now}");
});

app.MapGet("/characters/coffee-stats", async (IDocumentStore documentStore, [FromQuery] string id) =>
{
    using var session = documentStore.OpenAsyncSession();

    var coffeeData = await session.TimeSeriesFor(id, "coffee-drinks")
        .GetAsync(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow);
    var totalCups = await session.CountersFor(id).GetAsync("TotalCups");

    return Results.Ok(new { coffeeData, totalCups });
});

app.MapGet("/top-coffee-drinker", (IDocumentStore documentStore) =>
{
    using var session = documentStore.OpenSession();

    var result = session.Advanced.RawQuery<CounterResult>
            ("from characters as c where c.\"@metadata\".\"@counters\" == \"TotalCups\" " +
             "select { Name: c.Name, TotalCups: counter(c, \"TotalCups\") }")
            .ToList()
            .OrderByDescending(x => x.TotalCups)
            .FirstOrDefault();

    return Results.Ok($"The TOP coffee drinker is '{result?.Name}' with {result?.TotalCups} Total Cups!");
});

app.MapDefaultEndpoints();

app.Run();


public class Character
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public string? FavoriteDrink { get; set; }
}

public class CounterResult
{
    public long? TotalCups { get; set; }
    public string? Name { get; set; }
}

Great news! We’re all set to go! Let’s run the AppHost project and examine our API service together.

Testing the API with Postman

Once the AppHost and APIService projects run, we can test the API endpoints using Postman.

1. Adding Characters to the Database

  1. Open Postman.
  2. Create a new PUT request.
  3. Set the URL to `http://localhost:5000/characters` (adjust port as needed).
  4. Send the request.
  5. Verify that the characters have been successfully stored in RavenDB.

2. Logging Coffee Consumption

  1. Create a new POST request.
  2. Set the URL to `http://localhost:5000/characters/coffee?id=characters/joey`.
  3. Send the request multiple times and with different `id`s to log coffee drinks.

3. Fetching Coffee Stats

  1. Create a new GET request.
  2. Set the URL to `http://localhost:5000/characters/coffee-stats?id=characters/joey`.
  3. Send the request.
  4. Check the response for Joey’s coffee drinking trends.

4. Finding the Top Coffee Drinker!

  1. Create a new GET request.
  2. Set the URL to `http://localhost:5000/top-coffee-drinker`.
  3. Send the request.
  4. Verify that the character with the highest coffee consumption is returned (hint: it’s probably Chandler!).


Congratulations! 🎉 

You’ve now learned how to integrate .NET Aspire with RavenDB, configure hosting and client integrations, and build an API that efficiently tracks and queries data. 


Woah, already finished? 🤯

If you found the article interesting, don’t miss a chance to try our database solution – totally for free!

Try now try now arrow icon