From Monolith to Modern: A Detailed Guide to Integrating .NET Aspire
Migrating an existing .NET application to .NET Aspire can unlock powerful capabilities for local orchestration, service discovery, and observability. While it might seem daunting, the process is incremental and highly rewarding.
This guide provides a detailed, step-by-step walkthrough for adding .NET Aspire to your project. We’ll cover creating the necessary Aspire projects, wiring up your existing web app, adding a backend API for service-to-service communication, and connecting to resources like databases.
Step 1: Prerequisites and Template Installation
First, ensure your development environment is ready.
- .NET 9.0 SDK or later
- A container runtime: Docker Desktop or Podman must be running.
- An IDE: Visual Studio 2022 17.10+ or VS Code with the C# Dev Kit extension.
Next, install the .NET Aspire workload, which includes the necessary project templates. Open your terminal and run:
dotnet workload install aspire
To verify the installation, you can list the available templates:
dotnet new list aspire
You should see templates like aspire-apphost
and aspire-servicedefaults
.
Step 2: Create the AppHost and ServiceDefaults Projects
.NET Aspire uses two special projects to manage your application:
- AppHost Project: The orchestrator. It defines which projects, containers, and executables are part of your application and how they connect.
- ServiceDefaults Project: A shared class library that provides common, reusable configurations for telemetry, health checks, and resilience policies.
From the root directory of your solution, run the following commands:
# Create the AppHost project (e.g., YourProject.AppHost)
dotnet new aspire-apphost -o YourProject.AppHost
# Create the ServiceDefaults project (e.g., YourProject.ServiceDefaults)
dotnet new aspire-servicedefaults -o YourProject.ServiceDefaults
# Add the new projects to your solution file
dotnet sln add YourProject.AppHost/YourProject.AppHost.csproj
dotnet sln add YourProject.ServiceDefaults/YourProject.ServiceDefaults.csproj
Step 3: Integrate Your Existing Web App
Now, let’s bring your main web application into the Aspire ecosystem.
Reference ServiceDefaults
Your web app needs to reference ServiceDefaults
to inherit observability and health check features.
dotnet add Web/YourProject.csproj reference YourProject.ServiceDefaults/YourProject.ServiceDefaults.csproj
Update Program.cs
Open the Program.cs
file of your web app. You need to make two small additions:
builder.AddServiceDefaults()
: This extension method, called right afterWebApplication.CreateBuilder(args)
, configures OpenTelemetry, default health checks, and service discovery.app.MapDefaultEndpoints()
: This method, called beforeapp.Run()
, exposes the/health
and/alive
endpoints that the Aspire dashboard uses to monitor your service’s health.
// filepath: Web/Program.cs
var builder = WebApplication.CreateBuilder(args);
// 1. Enroll in Aspire's service defaults
builder.AddServiceDefaults();
// ... rest of your services configuration
var app = builder.Build();
// ... other middleware configuration
// 2. Map the default health and liveness endpoints
app.MapDefaultEndpoints();
app.Run();
Step 4: Configure the AppHost Orchestrator
The AppHost
project needs to know which projects to run. Add a reference to your web app in the AppHost
project.
dotnet add YourProject.AppHost/YourProject.AppHost.csproj reference Web/YourProject.csproj
Now, open YourProject.AppHost/Program.cs
and define your application’s composition.
// filepath: YourProject.AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);
// Add the API project
var api = builder.AddProject<Projects.YourProject_Api>("api");
// Add the web project and give it a reference to the API
var webApp = builder.AddProject<Projects.YourProject>("web")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health"); // Exposes the UI to be accessible from your browser
builder.Build().Run();
At this point, you have a runnable Aspire application! You can test it by running the AppHost
project.
Step 5: Adding and Connecting a Web API
Most applications have a separate backend API. Aspire makes service-to-service communication seamless. Let’s assume you have an API project named YourProject.Api
.
First, add the ServiceDefaults
reference to your API project and update its Program.cs
just as you did for the web app in Step 3.
Next, add a reference to the API project in your AppHost
:
dotnet add YourProject.AppHost/YourProject.AppHost.csproj reference Api/YourProject.Api.csproj
Now, update AppHost/Program.cs
to include the API and establish the communication link.
// filepath: YourProject.AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);
// Add the API project
var api = builder.AddProject<Projects.YourProject_Api>("api")
.WithHttpHealthCheck("/health");
// Add the web project and give it a reference to the API
var webApp = builder.AddProject<Projects.YourProject>("web")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health")
.WithReference(api) // This enables service discovery
.WaitFor(api);
builder.Build().Run();
The .WithReference(api)
call is key. Aspire’s service discovery will automatically inject an environment variable into the web
service’s container, mapping the logical name http://api
to the API’s actual address.
To use this in your web app, configure an HttpClient
to call the API.
// In your web app's Program.cs
builder.Services.AddHttpClient<ApiService>(client =>
{
// "api" is the name we gave the service in the AppHost
client.BaseAddress = new Uri("http://api");
});
Step 6: Connect to Resources (e.g., Azure SQL)
Aspire excels at managing dependencies like databases. It can provision resources for you, connect to existing ones, or run them in local containers for development. Let’s configure the AppHost
to use a SQL database and provide the connection string exclusively to the API project.
First, add the required Aspire hosting package to the AppHost
:
dotnet add YourProject.AppHost/YourProject.AppHost.csproj package Aspire.Hosting.Azure.Sql
Now, you have a choice depending on your environment.
Option A: Running SQL Server as a Local Container (for Development)
For local development, running a SQL Server instance in a container is fast and doesn’t require an Azure subscription. Aspire makes this simple with .RunAsContainer()
.
Update AppHost/Program.cs
:
// filepath: YourProject.AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);
// Configure SQL Server to run in a local container
var sqlServer = builder.AddAzureSqlServer("sql")
.RunAsContainer();
// Add a database to the containerized SQL instance
var yourProjectDb = sqlServer.AddDatabase("YourProjectDb");
// Add the API project and give it the DB connection
var api = builder.AddProject<Projects.YourProject_Api>("api")
.WithHttpHealthCheck("/health")
.WithReference(yourProjectDb)
.WaitFor(yourProjectDb);
// Add the web project and give it a reference to the API
var webApp = builder.AddProject<Projects.YourProject>("web")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health")
.WithReference(api)
.WaitFor(api);
builder.Build().Run();
Option B: Connecting to an Existing Azure SQL Server
When deploying to a staging or production environment, you’ll connect to a provisioned Azure SQL Server. The .AsExisting()
method tells Aspire to use your existing infrastructure.
Update AppHost/Program.cs
:
// filepath: YourProject.AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);
// Parameters for the existing Azure SQL Server
var sqlServerName = builder.AddParameter("sqlServerName");
var sqlServerResourceGroup = builder.AddParameter("sqlServerResourceGroup");
// Reference the existing Azure SQL Server
var sqlServer = builder.AddAzureSqlServer("sql")
.AsExisting(sqlServerName, sqlServerResourceGroup);
// Reference a database within that server
var yourProjectDb = sqlServer.AddDatabase("YourProjectDb");
// Add the API project and give it the DB connection
var api = builder.AddProject<Projects.YourProject_Api>("api")
.WithReference(yourProjectDb)
.WithHttpHealthCheck("/health")
.WaitFor(yourProjectDb);
// Add the web project and give it a reference to the API
var webApp = builder.AddProject<Projects.YourProject>("web")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health")
.WithReference(api)
.WaitFor(api);
builder.Build().Run();
When you run the AppHost, Aspire will prompt you to provide values for sqlServerName
and sqlServerResourceGroup
.
Consuming the Connection String
Regardless of which option you choose, Aspire makes the connection string available to your API project via dependency injection. The web application does not need a database connection.
First, add the Entity Framework Core package to your API project:
dotnet add Api/YourProject.Api.csproj package Aspire.Microsoft.EntityFrameworkCore.SqlServer
Then, in the Program.cs
of the Api
project, call AddSqlServerDbContext
. This extension method automatically finds the connection string that Aspire provides.
// In Program.cs for the Api project
builder.AddSqlServerDbContext<DatabaseContext>("YourProjectDb");
The string "YourProjectDb"
must match the name you gave the database in the AppHost
project.
Step 7: Run with Aspire!
You’re all set. Instead of running your individual projects, you now run the AppHost
project, which orchestrates everything.
From your IDE, set YourProject.AppHost
as the startup project and press F5. Or, from the terminal:
dotnet run --project YourProject.AppHost
This command will:
- Start the Azure SQL resource (if configured to run in a container).
- Build and run your API and Web App projects.
- Launch the Aspire Dashboard in your browser.
From the dashboard, you can view aggregated logs, distributed traces, and real-time metrics for your entire application, all with minimal configuration.
What’s Next: Deploying to Azure
Stay tuned for Part Two of this guide! We’ll take our fully integrated .NET Aspire application and show you how to deploy all the necessary resources to Azure seamlessly using the Azure Developer CLI (azd
). You’ll learn how to author the required infrastructure-as-code files and provision your entire application with a single azd up
command.