แก้ไข

แชร์ผ่าน


Orleans and .NET Aspire integration

.NET Aspire provides a streamlined approach to building cloud-native applications with built-in support for Orleans. Starting with Orleans 8.0, you can use Aspire to orchestrate your Orleans cluster, manage backing resources (like Redis or Azure Storage), and automatically configure service discovery, observability, and health checks.

Overview

Orleans integration with .NET Aspire uses the Aspire.Hosting.Orleans package in your AppHost project. This package provides extension methods to:

  • Define Orleans as a distributed resource
  • Configure clustering providers (Redis, Azure Storage, ADO.NET)
  • Configure grain storage providers
  • Configure reminder providers
  • Configure grain directory providers
  • Model silo and client relationships

Prerequisites

Before using Orleans with Aspire, ensure you have:

Required packages

Your solution needs the following package references:

AppHost project

<ItemGroup>
  <PackageReference Include="Aspire.Hosting.AppHost" Version="9.0.0" />
  <PackageReference Include="Aspire.Hosting.Orleans" Version="9.0.0" />
  <PackageReference Include="Aspire.Hosting.Redis" Version="9.0.0" />
</ItemGroup>

Orleans silo project

<ItemGroup>
  <PackageReference Include="Microsoft.Orleans.Server" Version="10.0.0" />
  <PackageReference Include="Microsoft.Orleans.Clustering.Redis" Version="10.0.0" />
  <PackageReference Include="Aspire.StackExchange.Redis" Version="9.0.0" />
</ItemGroup>

Orleans client project (if separate from silo)

<ItemGroup>
  <PackageReference Include="Microsoft.Orleans.Client" Version="10.0.0" />
  <PackageReference Include="Microsoft.Orleans.Clustering.Redis" Version="10.0.0" />
  <PackageReference Include="Aspire.StackExchange.Redis" Version="9.0.0" />
</ItemGroup>

Configure the AppHost

The AppHost project orchestrates your Orleans cluster and its dependencies.

Basic Orleans cluster with Redis clustering

public static void BasicOrleansCluster(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    // Add Redis for Orleans clustering
    var redis = builder.AddRedis("orleans-redis");

    // Define the Orleans resource with Redis clustering
    var orleans = builder.AddOrleans("cluster")
        .WithClustering(redis);

    // Add the Orleans silo project
    builder.AddProject<Projects.Silo>("silo")
        .WithReference(orleans)
        .WaitFor(redis)
        .WithReplicas(3);

    builder.Build().Run();
}

Orleans with grain storage and reminders

public static void OrleansWithStorageAndReminders(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    var redis = builder.AddRedis("orleans-redis");

    var orleans = builder.AddOrleans("cluster")
        .WithClustering(redis)
        .WithGrainStorage("Default", redis)
        .WithGrainStorage("PubSubStore", redis)
        .WithReminders(redis);

    builder.AddProject<Projects.Silo>("silo")
        .WithReference(orleans)
        .WaitFor(redis)
        .WithReplicas(3);

    builder.Build().Run();
}

Separate silo and client projects

When your Orleans client runs in a separate process (such as a web frontend), use the .AsClient() method:

public static void SeparateSiloAndClient(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    var redis = builder.AddRedis("orleans-redis");

    var orleans = builder.AddOrleans("cluster")
        .WithClustering(redis)
        .WithGrainStorage("Default", redis);

    // Backend Orleans silo cluster
    var silo = builder.AddProject<Projects.Silo>("backend")
        .WithReference(orleans)
        .WaitFor(redis)
        .WithReplicas(5);

    // Frontend web project as Orleans client
    builder.AddProject<Projects.Client>("frontend")
        .WithReference(orleans.AsClient())  // Client-only reference
        .WaitFor(silo);

    builder.Build().Run();
}

Configure the Orleans silo project

In your Orleans silo project, configure Orleans to use the Aspire-provided resources:

public static void BasicSiloConfiguration(string[] args)
{
    var builder = Host.CreateApplicationBuilder(args);

    // Add Aspire service defaults (OpenTelemetry, health checks, etc.)
    builder.AddServiceDefaults();

    // Add the Aspire Redis client for Orleans
    builder.AddKeyedRedisClient("orleans-redis");

    // Configure Orleans - Aspire injects all configuration automatically
    builder.UseOrleans();

    builder.Build().Run();
}

Tip

When using .NET Aspire, the parameterless UseOrleans is typically all you need. Aspire injects Orleans configuration (cluster ID, service ID, endpoints, and provider settings) via environment variables that Orleans reads automatically. You only need the delegate overload UseOrleans(siloBuilder => {...}) when you require additional manual configuration beyond what Aspire provides.

Important

You must call the appropriate AddKeyed* method (such as AddKeyedRedisClient, AddKeyedAzureTableClient, or AddKeyedAzureBlobClient) to register the backing resource in the dependency injection container. Orleans providers look up resources by their keyed service name—if you skip this step, Orleans won't be able to resolve the resource and will throw a dependency resolution error at runtime. This applies to all Aspire-managed resources used with Orleans.

Configure with explicit connection string

If you need explicit control over the connection string, you can read it from configuration:

public static void ExplicitConnectionConfiguration(string[] args)
{
    var builder = Host.CreateApplicationBuilder(args);

    builder.AddServiceDefaults();
    builder.AddKeyedRedisClient("orleans-redis");

    builder.UseOrleans(siloBuilder =>
    {
        var redisConnectionString = builder.Configuration.GetConnectionString("orleans-redis");

        siloBuilder.UseRedisClustering(options =>
        {
            options.ConfigurationOptions =
                ConfigurationOptions.Parse(redisConnectionString!);
        });

        siloBuilder.AddRedisGrainStorageAsDefault(options =>
        {
            options.ConfigurationOptions =
                ConfigurationOptions.Parse(redisConnectionString!);
        });
    });

    builder.Build().Run();
}

Configure the Orleans client project

For separate client projects, configure the Orleans client similarly:

public static void BasicClientConfiguration(string[] args)
{
    var builder = Host.CreateApplicationBuilder(args);

    builder.AddServiceDefaults();
    builder.AddKeyedRedisClient("orleans-redis");

    // Configure Orleans client - Aspire injects clustering configuration automatically
    builder.UseOrleansClient();

    builder.Build().Run();
}

AppHost extension methods reference

The Aspire.Hosting.Orleans package provides these extension methods:

Core methods

Method Description
builder.AddOrleans(name) Adds an Orleans resource to the distributed application with the specified name.
.WithClusterId(id) Sets the Orleans ClusterId. Accepts a string or ParameterResource. If not specified, a unique ID is generated automatically.
.WithServiceId(id) Sets the Orleans ServiceId. Accepts a string or ParameterResource. If not specified, a unique ID is generated automatically.
.AsClient() Returns a client-only reference to the Orleans resource (doesn't include silo capabilities).
project.WithReference(orleans) Adds the Orleans resource reference to a project, enabling configuration injection.

Note

When you configure a backing resource using .WithClustering(resource), .WithGrainStorage(name, resource), or similar methods, the Orleans resource automatically includes a reference to that backing resource. You don't need to call .WithReference() separately for each backing resource—only .WithReference(orleans) is required. However, you should use .WaitFor() on the backing resource to ensure it's ready before the silo starts.

Clustering

Method Description
.WithClustering(resource) Configures Orleans clustering to use the specified resource (Redis, Azure Storage, Cosmos DB, etc.).
.WithDevelopmentClustering() Configures in-memory, single-host clustering for local development only. Not suitable for production.

Grain storage

Method Description
.WithGrainStorage(name, resource) Configures a named grain storage provider using the specified resource.
.WithMemoryGrainStorage(name) Configures in-memory grain storage for the specified name. Data is lost on silo restart.

Reminders

Method Description
.WithReminders(resource) Configures the Orleans reminder service using the specified resource.
.WithMemoryReminders() Configures in-memory reminders for development. Reminders are lost on silo restart.

Streaming

Method Description
.WithStreaming(name, resource) Configures a named stream provider using the specified resource (e.g., Azure Queue Storage).
.WithMemoryStreaming(name) Configures in-memory streaming for development.
.WithBroadcastChannel(name) Configures a broadcast channel provider with the specified name.

Grain directory

Method Description
.WithGrainDirectory(name, resource) Configures a named grain directory using the specified resource.

Service defaults pattern

Aspire uses a ServiceDefaults project pattern to share common configuration across all projects. For Orleans, this typically includes:

OpenTelemetry configuration

public static IHostApplicationBuilder AddServiceDefaults(
    this IHostApplicationBuilder builder)
{
    builder.ConfigureOpenTelemetry();
    builder.AddDefaultHealthChecks();
    
    return builder;
}

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");  // Orleans metrics
        })
        .WithTracing(tracing =>
        {
            tracing.AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation()
                .AddSource("Microsoft.Orleans.Runtime")
                .AddSource("Microsoft.Orleans.Application");
        });

    return builder;
}

Azure Storage with Aspire

You can use Azure Storage resources for Orleans clustering and persistence:

public static void AzureStorageWithAspire(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    // Add Azure Storage for Orleans
    var storage = builder.AddAzureStorage("orleans-storage")
        .RunAsEmulator();  // Use Azurite emulator for local development

    var tables = storage.AddTables("orleans-tables");
    var blobs = storage.AddBlobs("orleans-blobs");

    var orleans = builder.AddOrleans("cluster")
        .WithClustering(tables)
        .WithGrainStorage("Default", blobs)
        .WithReminders(tables);

    builder.AddProject<Projects.Silo>("silo")
        .WithReference(orleans)
        .WaitFor(storage)
        .WithReplicas(3);

    builder.Build().Run();
}

Development vs. production configuration

Aspire makes it easy to switch between development and production configurations:

Local development (using emulators)

public static void LocalDevelopment(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    var redis = builder.AddRedis("orleans-redis");
    // Redis container runs automatically during development

    var orleans = builder.AddOrleans("cluster")
        .WithClustering(redis);

    // ...
}

Production (using managed services)

public static void ProductionConfig(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    // Use existing Azure Cache for Redis
    var redis = builder.AddConnectionString("orleans-redis");

    var orleans = builder.AddOrleans("cluster")
        .WithClustering(redis);

    // ...
}

Health checks

Aspire automatically configures health check endpoints. You can add Orleans-specific health checks:

public static void ConfigureHealthChecks(IHostApplicationBuilder builder)
{
    builder.Services.AddHealthChecks()
        .AddCheck<GrainHealthCheck>("orleans-grains")
        .AddCheck<SiloHealthCheck>("orleans-silo");
}

Best practices

  1. Use ServiceDefaults: Share common configuration (OpenTelemetry, health checks) across all projects using a ServiceDefaults project.

  2. Wait for dependencies: Always use .WaitFor() to ensure backing resources (Redis, databases) are ready before Orleans silos start.

  3. Configure replicas: Use .WithReplicas() to run multiple silo instances for fault tolerance and scalability.

  4. Separate client projects: For web frontends, use .AsClient() to configure Orleans client-only mode.

  5. Use emulators for development: Aspire can run Redis, Azure Storage (Azurite), and other dependencies locally using containers.

  6. Enable distributed tracing: Configure OpenTelemetry with Orleans source names to trace grain calls across the cluster.

See also

.NET Aspire integration was introduced in Orleans 8.0. For Orleans 7.0, you can still deploy to Aspire-orchestrated environments, but the dedicated Aspire.Hosting.Orleans package and its extension methods are not available.

Consider upgrading to Orleans 8.0 or later to take advantage of the Aspire integration features.

.NET Aspire integration is available in Orleans 8.0 and later. Orleans 3.x does not support .NET Aspire.