.NET Aspire Orleans integration

Orleans has built-in support for .NET Aspire. .NET Aspire's application model lets you describe the services, databases, and other resources and infrastructure in your app and how they relate to each other. Orleans provides a straightforward way to build distributed applications that are elastically scalable and fault-tolerant. You can use .NET Aspire to configure and orchestrate Orleans and its dependencies, such as by providing Orleans with cluster membership and storage.

Orleans is represented as a resource in .NET Aspire. Unlike other integrations, the Orleans integration doesn't create a container and doesn't require a separate client integration package. Instead you complete the Orleans configuration in the .NET Aspire app host project.

Note

This integration requires Orleans version 8.1.0 or later.

Hosting integration

The Orleans hosting integration models an Orleans service as the OrleansService type. To access this type and APIs, add the 📦 Aspire.Hosting.Orleans NuGet package in the app host project.

dotnet add package Aspire.Hosting.Orleans

For more information, see dotnet add package or Manage package dependencies in .NET applications.

Add an Orleans resource

In your app host project, call AddOrleans to add and return an Orleans service resource builder. The name provided to the Orleans resource is for diagnostic purposes. For most applications, a value of "default" suffices.

var orleans = builder.AddOrleans("default")

Use Azure storage for clustering tables and grain storage

In an Orleans app, the fundamental building block is a grain. Grains can have durable states. You must store the durable state for a grain somewhere. In a .NET Aspire application, Azure Blob Storage is one possible location.

Orleans hosts register themselves in a database and use that database to find each other and form a cluster. They store which servers are members of which silos in a database table. You can use either relational or NoSQL databases to store this information. In a .NET Aspire application, a popular choice to store this table is Azure Table Storage.

To configure Orleans with clustering and grain storage in Azure, install the 📦 Aspire.Hosting.Azure.Storage NuGet package in the app host project:

dotnet add package Aspire.Hosting.Azure.Storage

In your app host project, after you call AddOrleans, configure the Orleans resource with clustering and grain storage using the WithClustering and WithGrainStorage methods respectively:

// Add the resources which you will use for Orleans clustering and
// grain state storage.
var storage = builder.AddAzureStorage("storage").RunAsEmulator();
var clusteringTable = storage.AddTables("clustering");
var grainStorage = storage.AddBlobs("grain-state");

// Add the Orleans resource to the Aspire DistributedApplication
// builder, then configure it with Azure Table Storage for clustering
// and Azure Blob Storage for grain storage.
var orleans = builder.AddOrleans("default")
                     .WithClustering(clusteringTable)
                     .WithGrainStorage("Default", grainStorage);

The preceding code tells Orleans that any service referencing it must also reference the clusteringTable resource.

Add an Orleans server project in the app host

Now you can add a new project, enrolled in .NET Aspire orchestration, to your solution as an Orleans server. It will take part in the Orleans cluster as a silo with constituent grains. Reference the Orleans resource from your server project using WithReference(orleans). When you reference the Orleans resource from your service, those resources are also referenced:

// Add your server project and reference your 'orleans' resource from it.
// It can join the Orleans cluster as a silo.
// This implicitly adds references to the required resources.
// In this case, that is the 'clusteringTable' resource declared earlier.
builder.AddProject<Projects.OrleansServer>("silo")
       .WithReference(orleans)
       .WithReplicas(3);

Add an Orleans client project in the app host

Orleans clients communicate with grains hosted on Orleans servers. In a .NET Aspire app, for example, you might have a front-end Web site that calls grains in an Orleans cluster. Reference the Orleans resource from your Orleans client using WithReference(orleans.AsClient()).

// Reference the Orleans resource as a client from the 'frontend'
// project so that it can connect to the Orleans cluster.
builder.AddProject<Projects.OrleansClient>("frontend")
       .WithReference(orleans.AsClient())
       .WithExternalHttpEndpoints()
       .WithReplicas(3);

Create the Orleans server project

Now that the app host project is completed, you can implement the Orleans server project. Let's start by adding the necessary NuGet packages:

In the folder for the Orleans server project, run these commands:

dotnet add package Aspire.Azure.Data.Tables
dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Microsoft.Orleans.Server
dotnet add package Microsoft.Orleans.Persistence.AzureStorage
dotnet add package Microsoft.Orleans.Clustering.AzureStorage

Next, in the Program.cs file of your Orleans server project, add the Azure Storage blob and tables clients and then call UseOrleans.

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddKeyedAzureTableClient("clustering");
builder.AddKeyedAzureBlobClient("grain-state");
builder.UseOrleans();

The following code is a complete example of an Orleans server project, including a grain named CounterGrain:

using Orleans.Runtime;
using OrleansContracts;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddKeyedAzureTableClient("clustering");
builder.AddKeyedAzureBlobClient("grain-state");
builder.UseOrleans();

var app = builder.Build();

app.MapGet("/", () => "OK");

await app.RunAsync();

public sealed class CounterGrain(
    [PersistentState("count")] IPersistentState<int> count) : ICounterGrain
{
    public ValueTask<int> Get()
    {
        return ValueTask.FromResult(count.State);
    }

    public async ValueTask<int> Increment()
    {
        var result = ++count.State;
        await count.WriteStateAsync();
        return result;
    }
}

Create an Orleans client project

In the Orleans client project, add the same NuGet packages:

dotnet add package Aspire.Azure.Data.Tables
dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Microsoft.Orleans.Client
dotnet add package Microsoft.Orleans.Persistence.AzureStorage
dotnet add package Microsoft.Orleans.Clustering.AzureStorage

Next, in the Program.cs file of your Orleans client project, add the Azure table storage client and then call UseOrleansClient.

builder.AddKeyedAzureTableClient("clustering");
builder.UseOrleansClient();

The following code is a complete example of an Orleans client project. It calls the CounterGrain grain defined in the Orleans server example above:

using OrleansContracts;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddKeyedAzureTableClient("clustering");
builder.UseOrleansClient();

var app = builder.Build();

app.MapGet("/counter/{grainId}", async (IClusterClient client, string grainId) =>
{
    var grain = client.GetGrain<ICounterGrain>(grainId);
    return await grain.Get();
});

app.MapPost("/counter/{grainId}", async (IClusterClient client, string grainId) =>
{
    var grain = client.GetGrain<ICounterGrain>(grainId);
    return await grain.Increment();
});

app.UseFileServer();

await app.RunAsync();

Enabling OpenTelemetry

By convention, .NET Aspire solutions include a project for defining default configuration and behavior for your service. This project is called the service defaults project and templates create it with a name ending in ServiceDefaults. To configure Orleans for OpenTelemetry in .NET Aspire, apply configuration to your service defaults project following the Orleans observability guide.

Modify the ConfigureOpenTelemetry method to add the Orleans meters and tracing instruments. The following code snippet shows the modified Extensions.cs file from a service defaults project that includes metrics and traces from Orleans.

public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
{
    builder.Logging.AddOpenTelemetry(logging =>
    {
        logging.IncludeFormattedMessage = true;
        logging.IncludeScopes = true;
    });

    builder.Services.AddOpenTelemetry()
        .WithMetrics(metrics =>
        {
            metrics.AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation()
                .AddRuntimeInstrumentation()
                .AddMeter("Microsoft.Orleans");
        })
        .WithTracing(tracing =>
        {
            tracing.AddSource("Microsoft.Orleans.Runtime");
            tracing.AddSource("Microsoft.Orleans.Application");

            tracing.AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation();
        });

    builder.AddOpenTelemetryExporters();

    return builder;
}

Supported providers

The Orleans Aspire integration supports a limited subset of Orleans providers today:

  • Clustering:
    • Redis
    • Azure Storage Tables
  • Persistence:
    • Redis
    • Azure Storage Tables
    • Azure Storage Blobs
  • Reminders:
    • Redis
    • Azure Storage Tables
  • Grain directory:
    • Redis
    • Azure Storage Tables

Streaming providers aren't supported as of Orleans version 8.1.0.

Next steps