Use Orleans with .NET Aspire
Orleans has built-in support for .NET Aspire. .NET Aspire's application model lets you describe the services, databases, and other resources/infrastructure in your app and how they relate. Orleans provides a straightforward way to build distributed applications which are elastically scalable and fault-tolerant. .NET Aspire is used to configure and orchestrate Orleans and its dependencies, such as, by providing Orleans with database cluster membership and storage.
Orleans is represented as a resource in .NET Aspire. The Orleans resource includes configuration which your service needs to operate, such as cluster membership providers and storage providers.
Prerequisites
To work with .NET Aspire, you need the following installed locally:
- .NET 8.0 or .NET 9.0
- An OCI compliant container runtime, such as:
- Docker Desktop or Podman. For more information, see Container runtime.
- An Integrated Developer Environment (IDE) or code editor, such as:
- Visual Studio 2022 version 17.9 or higher (Optional)
- Visual Studio Code (Optional)
- C# Dev Kit: Extension (Optional)
- JetBrains Rider with .NET Aspire plugin (Optional)
For more information, see .NET Aspire setup and tooling, and .NET Aspire SDK.
In addition to the prerequisites for .NET Aspire, you need:
- Orleans version 8.1.0 or later
For more information, see .NET Aspire setup and tooling.
Get started
To get started, you need to add the Orleans hosting package to your app host project by installing the 📦 Aspire.Hosting.Orleans NuGet package.
dotnet add package Aspire.Hosting.Orleans
For more information, see dotnet add package or Manage package dependencies in .NET applications.
The Orleans resource is added to the .NET Aspire distributed application builder using the AddOrleans(string name)
method, which returns an Orleans 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")
The Orleans resource builder offers methods to configure your Orleans resource.
To configure Orleans with clustering and grain storage, install the 📦 Aspire.Hosting.Azure.Storage NuGet package in the app host project:
dotnet add package Aspire.Hosting.Azure.Storage
In the following example, the Orleans resource is configured 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 also needs to reference the clusteringTable
resource.
To participate in an Orleans cluster, reference the Orleans resource from your service project, either using WithReference(orleans)
to participate as an Orleans server, or WithReference(orleans.AsClient())
to participate as a client. When you reference the Orleans resource from your service, those resources are also referenced:
// Add our server project and reference your 'orleans' resource from it.
// it can join the Orleans cluster as a service.
// This implicitly add references to the required resources.
// In this case, that is the 'clusteringTable' resource declared earlier.
builder.AddProject<Projects.OrleansServer>("silo")
.WithReference(orleans)
.WithReplicas(3);
Putting that all together, here's an example of a .NET Aspire app host project which includes:
- An Orleans resource with clustering and storage.
- An Orleans server project, OrleansServer.
- An Orleans client project, OrleansClient.
var builder = DistributedApplication.CreateBuilder(args);
// 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);
// Add our server project and reference your 'orleans' resource from it.
// it can join the Orleans cluster as a service.
// This implicitly add references to the required resources.
// In this case, that is the 'clusteringTable' resource declared earlier.
builder.AddProject<Projects.OrleansServer>("silo")
.WithReference(orleans)
.WithReplicas(3);
// 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);
// Build and run the application.
using var app = builder.Build();
await app.RunAsync();
To consume the .NET Aspire Orleans resource from an Orleans server project, there are a few steps:
- Add the relevant .NET Aspire integrations. In this example, you're using Aspire.Azure.Data.Tables and Aspire.Azure.Storage.Blobs.
- Add the Orleans provider packages for those .NET Aspire integrations. In this example, you're using
Microsoft.Orleans.Persistence.AzureStorage
andMicrosoft.Orleans.Clustering.AzureStorage
. - Add Orleans to the host application builder.
In the Program.cs file of your Orleans server project, you must configure the .NET Aspire integrations you're using and add Orleans to the host builder. The names provided must match the names used in the .NET Aspire app host project: "clustering" for the clustering provider, and "grain-state" for the grain state storage provider:
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;
}
}
You do similarly in the OrleansClient project, adding the .NET Aspire resources which your project needs to join the Orleans cluster as a client, and configuring the host builder to add an Orleans client:
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 default project and templates create it with a name ending in ServiceDefaults. To configure Orleans for OpenTelemetry in .NET Aspire, you need to apply configuration to your service defaults project following the Orleans observability guide. In short, you need to 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.