Quickstart: Build your first .NET Aspire project

Cloud-native apps often require connections to various services such as databases, storage and caching solutions, messaging providers, or other web services. .NET Aspire is designed to streamline connections and configurations between these types of services. This quickstart shows how to create a .NET Aspire Starter Application template solution.

In this quickstart, you'll explore the following tasks:

  • Create a basic .NET app that is set up to use .NET Aspire.
  • Add and configure a .NET Aspire component to implement caching at project creation time.
  • Create an API and use service discovery to connect to it.
  • Orchestrate communication between a front end UI, a back end API, and a local Redis cache.

Prerequisites

To work with .NET Aspire, you need the following installed locally:

For more information, see .NET Aspire setup and tooling.

Create the .NET Aspire template

To create a new .NET Aspire Starter Application, you can use either Visual Studio, Visual Studio Code, or the .NET CLI.

Visual Studio provides .NET Aspire project templates that handle some initial setup configurations for you. Complete the following steps to create a project for this quickstart:

  1. At the top of Visual Studio, navigate to File > New > Project.

  2. In the dialog window, search for Aspire and select .NET Aspire Starter Application. Select Next.

    A screenshot of the .NET Aspire Starter Application template.

  3. On the Configure your new project screen:

    • Enter a Project Name of AspireSample.
    • Leave the rest of the values at their defaults and select Next.
  4. On the Additional information screen:

    • Make sure .NET 8.0 (Long Term Support) is selected.
    • Ensure that Use Redis for caching (requires a supported container runtime) is checked and select Create.
    • Optionally, you can select Create a tests project. For more information, see Testing .NET Aspire projects.

Visual Studio creates a new solution that is structured to use .NET Aspire.

Visual Studio Code provides .NET Aspire project templates that handle some initial setup configurations for you. Complete the following steps to create a project for this quickstart:

  1. From a new instance of Visual Studio Code (without a folder open), select Create .NET project button.

  2. Select the .NET Aspire Starter Application template.

    A screenshot of the .NET Aspire Starter Application template.

dotnet new aspire-starter --use-redis-cache --output AspireSample

For more information, see dotnet new. The .NET CLI creates a new solution that is structured to use .NET Aspire.

Test the app locally

The sample app is now ready for testing. You want to verify the following:

  • Weather data is retrieved from the API project using service discovery and displayed on the weather page.
  • Subsequent requests are handled via the output caching configured by the .NET Aspire Redis component.

In Visual Studio, set the AspireSample.AppHost project as the startup project by right-clicking on the project in the Solution Explorer and selecting Set as Startup Project. Then, press F5 to run the app.

If you haven't already trusted the ASP.NET Core localhost certificate, you will need to trust the certificate before running the app:

dotnet dev-certs https --trust

For more information, see Troubleshoot untrusted localhost certificate in .NET Aspire. For in-depth details about troubleshooting localhost certificates on Linux, see ASP.NET Core: GitHub repository issue #32842.

In Visual Studio Code, press F5 to run the app. You'll be prompted to select which language, and C# will be suggested, select C# and then select the AspireSample.AppHost project with the Default Configuration:

A screenshot of the Visual Studio Code run configuration for the AspireSample.AppHost project.

dotnet run --project AspireSample/AspireSample.AppHost

For more information, see dotnet run.

  1. The app displays the .NET Aspire dashboard in the browser. We'll look at the dashboard in more detail later. For now, find the webfrontend project in the list of resources and select the project's localhost endpoint.

    A screenshot of the .NET Aspire Dashboard, highlighting the webfrontend project's localhost endpoint.

    The home page of the webfrontend app displays "Hello, world!"

  2. Navigate from the home page to the weather page in the using the left side navigation. The weather page displays weather data. Make a mental note of some of the values represented in the forecast table.

  3. Continue occasionally refreshing the page for 10 seconds. Within 10 seconds, the cached data is returned. Eventually, a different set of weather data appears, since the data is randomly generated and the cache is updated.

The Weather page of the webfrontend app showing the weather data retrieved from the API.

🤓 Congratulations! You created and ran your first .NET Aspire project! To stop the app, you can close the browser window.

In Visual Studio, select the Stop Debugging from the Debug menu to stop the app.

In Visual Studio Code, press Shift + F5 to stop the app, or select the Stop button at the top center of the window:

A screenshot of the Visual Studio Code stop button.

Press Ctrl + C in the terminal window to stop the app.

Now let's investigate the structure and other features of your new .NET Aspire project.

Explore the .NET Aspire dashboard

When you run a .NET Aspire project, a dashboard launches that you use to monitor various parts of your app. The dashboard resembles the following screenshot:

A screenshot of the .NET Aspire Dashboard, depicting the Projects tab.

Visit each link on the left navigation to view different information about the .NET Aspire project:

  • Resources: Lists basic information for all of the individual .NET projects in your .NET Aspire project, such as the app state, endpoint addresses, and the environment variables that were loaded in.

  • Console: Displays the console output from each of the projects in your app.

  • Structured: Displays structured logs in table format. These logs support basic filtering, free-form search, and log level filtering as well. You should see logs from the apiservice and the webfrontend. You can expand the details of each log entry by selecting the View button on the right end of the row.

  • Traces: Displays the traces for your application, which can track request paths through your apps. Locate a request for /weather and select View on the right side of the page. The dashboard should display the request in stages as it travels through the different parts of your app.

    A screenshot showing an .NET Aspire dashboard trace for the webfrontend /weather route.

  • Metrics: Displays various instruments and meters that are exposed and their corresponding dimensions for your app. Metrics conditionally expose filters based on their available dimensions.

    A screenshot showing an Aspire dashboard metrics page for the webfrontend.

For more information, see .NET Aspire dashboard overview.

Understand the .NET Aspire solution structure

The solution consists of the following projects:

  • AspireSample.ApiService: An ASP.NET Core Minimal API project is used to provide data to the front end. This project depends on the shared AspireSample.ServiceDefaults project.
  • AspireSample.AppHost: An orchestrator project designed to connect and configure the different projects and services of your app. The orchestrator should be set as the Startup project, and it depends on the AspireSample.ApiService and AspireSample.Web projects.
  • AspireSample.ServiceDefaults: A .NET Aspire shared project to manage configurations that are reused across the projects in your solution related to resilience, service discovery, and telemetry.
  • AspireSample.Web: An ASP.NET Core Blazor App project with default .NET Aspire service configurations, this project depends on the AspireSample.ServiceDefaults project. For more information, see .NET Aspire service defaults.

Your AspireSample directory should resemble the following:

└───📂 AspireSample
     ├───📂 AspireSample.ApiService
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.ApiService.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.AppHost
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.AppHost.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.ServiceDefaults
     │    ├─── AspireSample.ServiceDefaults.csproj
     │    └─── Extensions.cs
     ├───📂 AspireSample.Web
     │    ├───📂 Components
     │    │    ├───📂 Layout
     │    │    │    ├─── MainLayout.razor
     │    │    │    ├─── MainLayout.razor.css
     │    │    │    ├─── NavMenu.razor
     │    │    │    └─── NavMenu.razor.css
     │    │    ├───📂 Pages
     │    │    │    ├─── Counter.razor
     │    │    │    ├─── Error.razor
     │    │    │    ├─── Home.razor
     │    │    │    └─── Weather.razor
     │    │    ├─── _Imports.razor
     │    │    ├─── App.razor
     │    │    └─── Routes.razor
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├───📂 wwwroot
     │    │    ├───📂 bootstrap
     │    │    │    ├─── bootstrap.min.css
     │    │    │    └─── bootstrap.min.css.map
     │    │    ├─── app.css
     │    │    └─── favicon.png
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.Web.csproj
     │    ├─── Program.cs
     │    └─── WeatherApiClient.cs
     └─── AspireSample.sln

Explore the starter projects

Each project in an .NET Aspire solution plays a role in the composition of your app. The *.Web project is a standard ASP.NET Core Blazor App that provides a front end UI. For more information, see New Blazor Web App template. The *.ApiService project is a standard ASP.NET Core Minimal API template project. Both of these projects depend on the *.ServiceDefaults project, which is a shared project that's used to manage configurations that are reused across projects in your solution.

The two projects of interest in this quickstart are the *.AppHost and *.ServiceDefaults projects detailed in the following sections.

.NET Aspire host project

The *.AppHost project is responsible for acting as the orchestrator, and sets the IsAspireHost property of the project file to true:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsAspireHost>true</IsAspireHost>
    <UserSecretsId>1b1a6997-c2dd-47d0-b3fa-46811f182483</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\AspireSample.ApiService\AspireSample.ApiService.csproj" />
    <ProjectReference Include="..\AspireSample.Web\AspireSample.Web.csproj" />
  </ItemGroup>

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

</Project>

Consider the Program.cs file of the AspireSample.AppHost project:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

var apiService = builder.AddProject<Projects.AspireSample_ApiService>("apiservice");

builder.AddProject<Projects.AspireSample_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(cache)
    .WithReference(apiService);

builder.Build().Run();

If you've used either the .NET Generic Host or the ASP.NET Core Web Host before, the app host programming model and builder pattern should be familiar to you. The preceding code:

  • Creates an IDistributedApplicationBuilder instance from calling DistributedApplication.CreateBuilder().
  • Calls AddRedis with the name "cache" to add a Redis server to the app, assigning the returned value to a variable named cache, which is of type IResourceBuilder<RedisResource>.
  • Calls AddProject given the generic-type parameter with the project's details, adding the AspireSample.ApiService project to the application model. This is one of the fundamental building blocks of .NET Aspire, and it's used to configure service discovery and communication between the projects in your app. The name argument "apiservice" is used to identify the project in the application model, and used later by projects that want to communicate with it.
  • Calls AddProject again, this time adding the AspireSample.Web project to the application model. It also chains multiple calls to WithReference passing the cache and apiservice variables. The WithReference API is another fundamental API of .NET Aspire, which injects either service discovery information or connection string configuration into the project being added to the application model.

Finally, the app is built and run. The DistributedApplication.Run() method is provided by the .NET Aspire SDK, and is responsible for starting the app and all of its dependencies. For more information, see .NET Aspire orchestration overview.

.NET Aspire service defaults project

The *.ServiceDefaults project is a shared project that's used to manage configurations that are reused across the projects in your solution. This project ensures that all dependent services share the same resilience, service discovery, and OpenTelemetry configuration. A shared .NET Aspire project file contains the IsAspireSharedProject property set as true:

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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsAspireSharedProject>true</IsAspireSharedProject>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />

    <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.1" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.1" />
  </ItemGroup>

</Project>

The service defaults project exposes an extension method on the IHostApplicationBuilder type, named AddServiceDefaults. The service defaults project from the template is a starting point, and you can customize it to meet your needs. For more information, see .NET Aspire service defaults.

Orchestrate service communication

.NET Aspire provides orchestration features to assist with configuring connections and communication between the different parts of your app. The AspireSample.AppHost project added the AspireSample.ApiService and AspireSample.Web projects to the application model. It also declared their names as "webfrontend" for Blazor front end, "apiservice" for the API project reference. Additionally, a Redis server resource labelled "cache" was added. These names are used to configure service discovery and communication between the projects in your app.

The front end app defines a typed HttpClient that's used to communicate with the API project.

namespace AspireSample.Web;

public class WeatherApiClient(HttpClient httpClient)
{
    public async Task<WeatherForecast[]> GetWeatherAsync(
        int maxItems = 10,
        CancellationToken cancellationToken = default)
    {
        List<WeatherForecast>? forecasts = null;

        await foreach (var forecast in
            httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>(
                "/weatherforecast", cancellationToken))
        {
            if (forecasts?.Count >= maxItems)
            {
                break;
            }
            if (forecast is not null)
            {
                forecasts ??= [];
                forecasts.Add(forecast);
            }
        }

        return forecasts?.ToArray() ?? [];
    }
}

public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

The HttpClient is configured to use service discovery, consider the following code from the Program.cs file of the AspireSample.Web project:

using AspireSample.Web;
using AspireSample.Web.Components;

var builder = WebApplication.CreateBuilder(args);

// Add service defaults & Aspire components.
builder.AddServiceDefaults();
builder.AddRedisOutputCache("cache");

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    {
        // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
        // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
        client.BaseAddress = new("https+http://apiservice");
    });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();

The preceding code:

  • Calls AddServiceDefaults, configuring the shared defaults for the app.
  • Calls AddRedisOutputCache with the same connectionName that was used when adding the Redis container "cache" to the application model. This configures the app to use Redis for output caching.
  • Calls AddHttpClient and configures the HttpClient.BaseAddress to be "https+http://apiservice". This is the name that was used when adding the API project to the application model, and with service discovery configured, it will automatically resolve to the correct address to the API project.

For more information, see Make HTTP requests with the HttpClient class.

See also

Next steps