# Dotnet Aspire Patterns

> Orchestrates .NET Aspire apps. AppHost, service discovery, components, dashboard, health checks.

- Skill: `rudironsoni/dotnet-aspire-patterns` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add rudironsoni/dotnet-aspire-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rudironsoni/dotnet-aspire-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- License: MIT
- Author: rudironsoni (https://skillmd.com/u/rudironsoni)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/rudironsoni/dotnet-aspire-patterns

---


# dotnet-aspire-patterns

.NET Aspire orchestration patterns for building cloud-ready distributed applications. Covers AppHost configuration,
service discovery, the component model for integrating backing services (databases, caches, message brokers), the Aspire
dashboard for local observability, distributed health checks, and when to choose Aspire vs manual container
orchestration.

## Scope

- AppHost orchestration and distributed application topology
- Service discovery and resource references
- Aspire components for backing services (databases, caches, brokers)
- Aspire dashboard for local traces, logs, and metrics
- Distributed health checks

## Out of scope

- Raw Dockerfile authoring and multi-stage builds -- see [skill:dotnet-containers]
- Kubernetes manifests, Helm charts, and Docker Compose -- see [skill:dotnet-container-deployment]
- OpenTelemetry SDK configuration and custom metrics -- see [skill:dotnet-observability]
- DI service lifetime mechanics -- see [skill:dotnet-csharp-dependency-injection]
- Background service hosting -- see [skill:dotnet-background-services]

Cross-references: [skill:dotnet-containers] for container image optimization and base image selection,
[skill:dotnet-container-deployment] for production Kubernetes/Compose deployment, [skill:dotnet-observability] for
OpenTelemetry details beyond Aspire defaults, [skill:dotnet-csharp-dependency-injection] for DI fundamentals,
[skill:dotnet-background-services] for hosted service lifecycle patterns.

---

## Aspire Overview

.NET Aspire is an opinionated stack for building observable, production-ready distributed applications. It provides:

- **Orchestration** -- define your distributed topology in C# (the AppHost)
- **Components** -- pre-configured NuGet packages for common backing services
- **Service Defaults** -- shared configuration for OpenTelemetry, health checks, resilience
- **Dashboard** -- local development UI for traces, logs, metrics, and resource status

Aspire is not a deployment target. It orchestrates the local development and testing experience. For production, it
generates manifests consumed by deployment tools (Azure Developer CLI, Kubernetes, etc.).

### When to Use Aspire

| Scenario                                        | Recommendation                                                            |
| ----------------------------------------------- | ------------------------------------------------------------------------- |
| Multiple .NET services + backing infrastructure | Aspire AppHost -- simplifies local dev and service wiring                 |
| Single API with a database                      | Optional -- Aspire adds overhead for simple topologies                    |
| Non-.NET services only (Node, Python)           | Aspire can reference container images, but the tooling benefit is reduced |
| Need Kubernetes/Compose for local dev already   | Evaluate migration cost; Aspire replaces docker-compose for dev scenarios |
| Team needs consistent observability defaults    | Aspire ServiceDefaults standardize OTel across all projects               |

---

## AppHost Configuration

The AppHost is a .NET project (`Aspire.Hosting.AppHost` SDK) that defines the distributed application topology. It
references other projects and backing services, wiring them together with service discovery.

### AppHost Project Setup

````xml

<Project Sdk="Microsoft.NET.Sdk">

  <!-- Aspire SDK version is independent of .NET TFM; 9.x works on net8.0+ -->
  <Sdk Name="Aspire.AppHost.Sdk" Version="9.1.*" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <IsAspireHost>true</IsAspireHost>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.AppHost" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.Redis" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.RabbitMQ" Version="9.1.*" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyApi\MyApi.csproj" />
    <ProjectReference Include="..\MyWorker\MyWorker.csproj" />
  </ItemGroup>

</Project>

```csharp

### Defining the Topology

```csharp

var builder = DistributedApplication.CreateBuilder(args);

// Backing services -- Aspire manages containers automatically
var postgres = builder.AddPostgres("pg")
    .WithPgAdmin()              // Adds pgAdmin UI container
    .AddDatabase("ordersdb");

var redis = builder.AddRedis("cache")
    .WithRedisCommander();      // Adds Redis Commander UI

var rabbitmq = builder.AddRabbitMQ("messaging")
    .WithManagementPlugin();    // Adds RabbitMQ management UI

// Application projects -- wired with service discovery
var api = builder.AddProject<Projects.MyApi>("api")
    .WithReference(postgres)
    .WithReference(redis)
    .WithReference(rabbitmq)
    .WithExternalHttpEndpoints();  // Marks endpoints as public in deployment manifests

builder.AddProject<Projects.MyWorker>("worker")
    .WithReference(postgres)
    .WithReference(rabbitmq)
    .WaitFor(api);              // Start worker after API is healthy

builder.Build().Run();

```text

### Resource Lifecycle

`WaitFor` controls startup ordering. Resources wait until dependencies report healthy before starting:

```csharp

// Worker waits for both the database and API to be ready
builder.AddProject<Projects.MyWorker>("worker")
    .WithReference(postgres)
    .WaitFor(postgres)          // Wait for database container health check
    .WaitFor(api);              // Wait for API health endpoint

```text

Without `WaitFor`, resources start in parallel. Use it only when startup order matters (e.g., a worker that requires the
database schema to exist).

---

## Service Discovery

Aspire automatically configures service discovery so projects can resolve each other by resource name rather than
hardcoded URLs.

### How It Works

1. The AppHost injects endpoint information as environment variables and configuration
2. The `Aspire.ServiceDefaults` project configures `Microsoft.Extensions.ServiceDiscovery`
3. Application code resolves services by name via `HttpClient` or connection strings

### Consuming Discovered Services

```csharp

// In MyApi/Program.cs
var builder = WebApplication.CreateBuilder(args);

// AddServiceDefaults registers service discovery, OpenTelemetry, health checks
builder.AddServiceDefaults();

// HttpClient resolves "worker" via service discovery
builder.Services.AddHttpClient("worker-client", client =>
{
    client.BaseAddress = new Uri("https+http://worker");
});

```text

The `https+http://` scheme prefix tells the service discovery provider to try HTTPS first, falling back to HTTP. This is
the recommended pattern for inter-service communication in Aspire.

### Connection Strings

For backing services (databases, caches), Aspire injects connection strings via the standard `ConnectionStrings`
configuration section:

```csharp

// AppHost: .WithReference(postgres) on the API project
// injects ConnectionStrings__ordersdb automatically

// In MyApi/Program.cs
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb");
// Resolves ConnectionStrings:ordersdb from configuration

```csharp

---

## Component Model

Aspire components are NuGet packages that provide pre-configured client integrations for backing services. They handle
connection management, health checks, telemetry, and resilience.

### Hosting Packages vs Client Packages

| Package Type        | Installed In     | Purpose                                                   |
| ------------------- | ---------------- | --------------------------------------------------------- |
| `Aspire.Hosting.*`  | AppHost project  | Define and configure the resource (container, connection) |
| `Aspire.* (client)` | Service projects | Consume the resource with health checks and telemetry     |

```xml

<!-- AppHost project -->
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.*" />

<!-- API project -->
<PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.1.*" />

```text

### Common Components

| Component            | Hosting Package                   | Client Package                                   |
| -------------------- | --------------------------------- | ------------------------------------------------ |
| PostgreSQL (EF Core) | `Aspire.Hosting.PostgreSQL`       | `Aspire.Npgsql.EntityFrameworkCore.PostgreSQL`   |
| PostgreSQL (Npgsql)  | `Aspire.Hosting.PostgreSQL`       | `Aspire.Npgsql`                                  |
| Redis (caching)      | `Aspire.Hosting.Redis`            | `Aspire.StackExchange.Redis`                     |
| Redis (output cache) | `Aspire.Hosting.Redis`            | `Aspire.StackExchange.Redis.OutputCaching`       |
| RabbitMQ             | `Aspire.Hosting.RabbitMQ`         | `Aspire.RabbitMQ.Client`                         |
| Azure Service Bus    | `Aspire.Hosting.Azure.ServiceBus` | `Aspire.Azure.Messaging.ServiceBus`              |
| SQL Server (EF Core) | `Aspire.Hosting.SqlServer`        | `Aspire.Microsoft.EntityFrameworkCore.SqlServer` |
| MongoDB              | `Aspire.Hosting.MongoDB`          | `Aspire.MongoDB.Driver`                          |

### Client Registration

```csharp

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// Each Add* method registers the client, health check, and telemetry
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb");
builder.AddRedisClient("cache");
builder.AddRabbitMQClient("messaging");

```text

Component `Add*` methods:

1. Register the client/DbContext in DI
2. Add a health check for the resource
3. Configure OpenTelemetry instrumentation for the client
4. Apply default resilience settings (retries, timeouts)

---

## Service Defaults

The `ServiceDefaults` project is a shared library referenced by all service projects. It standardizes cross-cutting
concerns.

### What ServiceDefaults Configures

```csharp

public static class Extensions
{
    public static IHostApplicationBuilder AddServiceDefaults(
        this IHostApplicationBuilder builder)
    {
        // Service discovery
        builder.ConfigureOpenTelemetry();
        builder.AddDefaultHealthChecks();
        builder.Services.AddServiceDiscovery();

        // Resilience for HttpClient
        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            http.AddStandardResilienceHandler();
            http.AddServiceDiscovery();
        });

        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())
            .WithTracing(tracing => tracing
                .AddAspNetCoreInstrumentation()
                .AddGrpcClientInstrumentation()
                .AddHttpClientInstrumentation());

        builder.AddOpenTelemetryExporters();
        return builder;
    }

    public static IHostApplicationBuilder AddDefaultHealthChecks(
        this IHostApplicationBuilder builder)
    {
        builder.Services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy());

        return builder;
    }

    public static WebApplication MapDefaultEndpoints(
        this WebApplication app)
    {
        app.MapHealthChecks("/health");
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });

        return app;
    }
}

```text

### Using ServiceDefaults

Every service project references the ServiceDefaults project and calls the extension methods:

```csharp

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// ... service-specific registrations

var app = builder.Build();
app.MapDefaultEndpoints();

// ... middleware and endpoints

app.Run();

```text

---

## Dashboard

The Aspire dashboard provides a local observability UI that starts automatically with the AppHost. It displays:

- **Resources** -- status of all projects, containers, and executables
- **Console logs** -- aggregated stdout/stderr from all resources
- **Structured logs** -- OpenTelemetry log records with structured properties
- **Traces** -- distributed traces across all services
- **Metrics** -- real-time metric charts

### Accessing the Dashboard

When you run the AppHost (`dotnet run --project MyApp.AppHost`), the dashboard URL is printed to the console:

```text

info: Aspire.Hosting.DistributedApplication[0]
      Login to the dashboard at https://localhost:17043/login?t=<token>

```text

### Dashboard in Non-Aspire Projects

The dashboard is available as a standalone container for projects not using the full Aspire stack:

```bash

docker run --rm -it -p 18888:18888 -p 4317:18889 \
  -d --name aspire-dashboard \
  mcr.microsoft.com/dotnet/aspire-dashboard:9.1

## Detailed Examples

See [references/detailed-examples.md](references/detailed-examples.md) for complete code samples and advanced patterns.

