AgentSkillsCN

aspire

在.NET中使用.NET Aspire构建云原生分布式应用时使用。涵盖应用主机编排模型、服务默认设置、内置组件(Redis、PostgreSQL、RabbitMQ)、仪表盘、健康检查,以及部署至Azure Container Apps。 适用范围:编排多服务的.NET应用、只需一行代码即可添加Redis/PostgreSQL/RabbitMQ、内置OpenTelemetry可观测性、为分布式应用提供本地开发仪表盘、在项目间实现服务发现。 不适用范围:无外部依赖的单项目单体应用、使用Helm进行Kubernetes原生部署(使用标准容器)、无需.NET编排的非.NET多语言微服务、Dapr侧边栏架构(使用dapr)。

SKILL.md
--- frontmatter
name: aspire
description: |
  Use when building cloud-native distributed applications with .NET Aspire. Covers the app host orchestration model, service defaults, built-in components (Redis, PostgreSQL, RabbitMQ), dashboard, health checks, and deployment to Azure Container Apps.
  USE FOR: orchestrating multi-service .NET applications, adding Redis/PostgreSQL/RabbitMQ with one line, built-in OpenTelemetry observability, local development dashboard for distributed apps, service discovery between projects
  DO NOT USE FOR: single-project monolithic applications without external dependencies, Kubernetes-native deployments with Helm (use standard containers), non-.NET polyglot microservices without .NET orchestration, Dapr sidecar architecture (use dapr)
license: MIT
metadata:
  displayName: ".NET Aspire"
  author: "Tyler-R-Kendrick"
  version: "1.0.0"
compatibility: claude, copilot, cursor

.NET Aspire

Overview

.NET Aspire is an opinionated framework for building observable, production-ready, cloud-native distributed applications. It provides an app host project for orchestrating multi-service applications, service defaults for consistent configuration (OpenTelemetry, health checks, resilience), and pre-built components for common infrastructure like Redis, PostgreSQL, RabbitMQ, and Azure services. The Aspire dashboard provides real-time visibility into traces, logs, and metrics during local development.

Project Structure

code
MyApp/
  MyApp.AppHost/           # Orchestration project (references all services)
  MyApp.ServiceDefaults/   # Shared configuration (telemetry, health checks)
  MyApp.Api/               # Web API project
  MyApp.Worker/            # Background worker project
  MyApp.Web/               # Frontend project

Getting Started

bash
dotnet new aspire-starter -n MyApp
# Creates AppHost, ServiceDefaults, Api, and Web projects

App Host (Orchestration)

The AppHost project defines the distributed application topology: which services to run, what infrastructure they need, and how they connect.

csharp
// MyApp.AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);

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

var postgres = builder.AddPostgres("postgres")
    .WithPgAdmin()          // Adds pgAdmin UI
    .WithDataVolume();      // Persists data across restarts

var db = postgres.AddDatabase("appdb");

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

// Application projects
var api = builder.AddProject<Projects.MyApp_Api>("api")
    .WithReference(db)
    .WithReference(cache)
    .WithReference(rabbit)
    .WithExternalHttpEndpoints();

builder.AddProject<Projects.MyApp_Worker>("worker")
    .WithReference(db)
    .WithReference(rabbit);

builder.AddProject<Projects.MyApp_Web>("web")
    .WithReference(api)
    .WithExternalHttpEndpoints();

builder.Build().Run();

Service Defaults

Shared configuration that every service in the application uses.

csharp
// MyApp.ServiceDefaults/Extensions.cs
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

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

        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()
                    .AddHttpClientInstrumentation()
                    .AddEntityFrameworkCoreInstrumentation();
            });

        builder.AddOpenTelemetryExporters();
        return builder;
    }

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

        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;
    }
}

Service Project Configuration

csharp
// MyApp.Api/Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

// Aspire component for PostgreSQL with EF Core
builder.AddNpgsqlDbContext<AppDbContext>("appdb");

// Aspire component for Redis caching
builder.AddRedisDistributedCache("cache");

// Aspire component for RabbitMQ
builder.AddRabbitMQClient("messaging");

builder.Services.AddControllers();

var app = builder.Build();

app.MapDefaultEndpoints();
app.MapControllers();
app.Run();

Built-in Components

ComponentNuGet PackageAddXxx Method
PostgreSQL (EF Core)Aspire.Npgsql.EntityFrameworkCoreAddNpgsqlDbContext<T>
PostgreSQL (Npgsql)Aspire.NpgsqlAddNpgsqlDataSource
Redis (Caching)Aspire.StackExchange.Redis.DistributedCachingAddRedisDistributedCache
Redis (Output Cache)Aspire.StackExchange.Redis.OutputCachingAddRedisOutputCache
RabbitMQAspire.RabbitMQ.ClientAddRabbitMQClient
Azure Blob StorageAspire.Azure.Storage.BlobsAddAzureBlobClient
Azure Service BusAspire.Azure.Messaging.ServiceBusAddAzureServiceBusClient
Azure Key VaultAspire.Azure.Security.KeyVaultAddAzureKeyVaultClient
MongoDBAspire.MongoDB.DriverAddMongoDBClient
Seq (Logging)Aspire.SeqAddSeqEndpoint

Using Aspire Components in Services

csharp
// PostgreSQL with Entity Framework Core
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddNpgsqlDbContext<CatalogDbContext>("catalogdb");

var app = builder.Build();

app.MapGet("/products", async (CatalogDbContext db) =>
    await db.Products.ToListAsync());

app.MapPost("/products", async (Product product, CatalogDbContext db) =>
{
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Results.Created($"/products/{product.Id}", product);
});

// Redis distributed caching
app.MapGet("/products/{id}", async (
    int id, CatalogDbContext db, IDistributedCache cache) =>
{
    var cacheKey = $"product:{id}";
    var cached = await cache.GetStringAsync(cacheKey);
    if (cached is not null)
        return Results.Ok(JsonSerializer.Deserialize<Product>(cached));

    var product = await db.Products.FindAsync(id);
    if (product is null) return Results.NotFound();

    await cache.SetStringAsync(cacheKey,
        JsonSerializer.Serialize(product),
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
        });

    return Results.Ok(product);
});

Service Discovery and HTTP Clients

csharp
// In the API project, call another service by name
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

builder.Services.AddHttpClient<CatalogClient>(client =>
{
    // "catalog-api" is resolved via service discovery from the AppHost
    client.BaseAddress = new Uri("https+http://catalog-api");
});

var app = builder.Build();

app.MapGet("/orders/{id}/details", async (int id, CatalogClient catalog) =>
{
    var products = await catalog.GetProductsForOrderAsync(id);
    return Results.Ok(products);
});

public class CatalogClient(HttpClient httpClient)
{
    public async Task<List<Product>> GetProductsForOrderAsync(int orderId) =>
        await httpClient.GetFromJsonAsync<List<Product>>(
            $"/api/orders/{orderId}/products") ?? [];
}

Custom Resources

csharp
// Add a container resource for services not built into Aspire
var elasticsearch = builder.AddContainer("elasticsearch", "elasticsearch", "8.12.0")
    .WithHttpEndpoint(port: 9200, targetPort: 9200)
    .WithEnvironment("discovery.type", "single-node")
    .WithEnvironment("xpack.security.enabled", "false")
    .WithVolume("es-data", "/usr/share/elasticsearch/data");

builder.AddProject<Projects.MyApp_Api>("api")
    .WithReference(elasticsearch);

Environment and Parameters

csharp
var builder = DistributedApplication.CreateBuilder(args);

// Parameters (prompted at deploy time or from config)
var adminPassword = builder.AddParameter("admin-password", secret: true);

var postgres = builder.AddPostgres("postgres", password: adminPassword);

// Connection strings from configuration
var apiKey = builder.AddParameter("api-key", secret: true);

builder.AddProject<Projects.MyApp_Api>("api")
    .WithEnvironment("ApiKey", apiKey);

Deployment to Azure Container Apps

bash
# Install the azd CLI
winget install microsoft.azd

# Initialize and deploy
azd init
azd up
csharp
// AppHost with Azure-specific resources
var builder = DistributedApplication.CreateBuilder(args);

var insights = builder.AddAzureApplicationInsights("insights");
var storage = builder.AddAzureStorage("storage");
var blobs = storage.AddBlobs("blobs");

builder.AddProject<Projects.MyApp_Api>("api")
    .WithReference(insights)
    .WithReference(blobs);

Best Practices

  • Use the AppHost project solely for orchestration (defining resources, references, and endpoints); never put business logic, controllers, or domain code in the AppHost.
  • Call builder.AddServiceDefaults() as the first line in every service project's Program.cs to ensure consistent OpenTelemetry, health checks, resilience, and service discovery configuration.
  • Use WithDataVolume() on database resources (PostgreSQL, Redis) during development to persist data across container restarts rather than re-seeding on every startup.
  • Prefer Aspire's AddNpgsqlDbContext<T>("name") over manual DbContext configuration because it automatically wires connection strings from service discovery, configures health checks, and adds OpenTelemetry instrumentation.
  • Use WithExternalHttpEndpoints() only on services that need external access (API gateways, frontend); internal services should only be reachable through service discovery.
  • Reference services by name (https+http://catalog-api) in HttpClient.BaseAddress and let Aspire's service discovery resolve the actual endpoint, rather than hardcoding localhost:PORT URLs.
  • Use builder.AddParameter("key", secret: true) for sensitive values (API keys, passwords) instead of hardcoding them; Aspire prompts for these during azd up and stores them securely.
  • Add WithManagementPlugin(), WithPgAdmin(), or WithRedisCommander() during development to get admin UIs for infrastructure resources; these are automatically excluded in production deployments.
  • Run azd init and azd up for deploying to Azure Container Apps rather than manually building Docker images and configuring infrastructure.
  • Open the Aspire Dashboard (automatically launched during dotnet run on the AppHost) to inspect distributed traces, structured logs, and metrics across all services during local development.