.NET Aspire

Henrique Siebert Domareski
7 min readDec 4, 2023

--

.NET Aspire is a cloud ready stack for building observable, production ready, distributed applications. It is designed to improve the experience of building .NET cloud-native apps, and it provides a consistent, opinionated set of tools and patterns that help you build and run distributed apps. In this article, I present how to get started with .NET Aspire.

.NET Aspire makes it simpler to build cloud-native apps and provides a series of useful and important features such as resilience, service discovery, telemetry and health checks. Not that this is still a Preview version, the official release is planned for the first half of 2024.

To create a .NET Aspire project, you need to have .NET 8 SDK, Visual Studio 2022 Preview version and Docker Desktop (docker is only necessary if you want to create the project using containers. In this example I’m going to use it for the Redis cache). In VS you can search for Aspire and the following options will appear:

  • The .NET Aspire Application is a basic starter template that includes only the AppHost and ServiceDefaults projects. This template is designed to only provide the essentials for you to build off of.
  • .NET Aspire Starter Application is a template that includes the AppHost and ServiceDefaults projects, but also includes the Web project (a boilerplate UI) and the ApiService project (an API project).

For demonstration purposes, I created the project using the Starter template, and I selected the framework .NET 8.0 and selected the option to use Redis for caching:

After creating the project, you can see the following structure:

  • ApiService: This is an ASP.NET Core Minimal API project, used to provide data to the front end. This project depends on the shared AspireSample.ServiceDefaults project.
  • AppHost: This is an orchestrator project designed to connect and configure the different projects and services of your app. This project handles running all of the projects that are part of the .NET Aspire application .The orchestrator should be set as the Startup project, and it depends on the AspireSample.ApiService and AspireSample.Web projects. This is the project responsible for running the applications inside of this solution.
  • ServiceDefaults: This is a.NET Aspire shared project to manage configurations that are reused across the projects in your solution related to resilience, service discovery, and telemetry. This project ensures that all dependent services share the same resilience, service discovery, and OpenTelemetry configuration.
  • Web: This is a project that provides a front-end UI, it is an ASP.NET Core Blazor App project with default .NET Aspire service configurations, this project depends on the AspireSample.ServiceDefaults project.

AppHost Details

On the csproj of AppHost, there is the configuration to set this project as the orchestrator (see the property IsAspireHost):

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

In the Program.cs file on the AppHost project, you can see the following code that describes an application with two projects and a Redis cache:

In this example, the webfrontend project references the apiservice project. The WithReference calls instruct the .NET Aspire application to pass service discovery information for the referenced projects (apiservice) into the webfrontend project. Here are more details about the code on this file:

  • On line 1, it creates an IDistributedApplicationBuilder instance.
  • On line 3, it creates a Redis container to the app, assigning the returned value to the variable named cache.
  • On line 5, it calls the AddProject, adding the ApiService project to the application model. Here is where you configure service discovery and communication between the projects in your app. The name argument "apiservice" is used to identify the project (and be referenced) in the application model, and used later by projects that want to communicate with it.
  • On lines 7 up to 9, there is another call to AddProject, now to add the Web project to the application model. This also includes another calls using the WithReference, passing the cache and the apiservice variables. The WithReference API injects either service discovery information or connection string configuration into the project being added to the application model.
  • On line 11, the app is built and run.

ServiceDefaults Details

On the ServiceDefaults project, in the csproj you can find the configuration for the IsAspireSharedProject property (on the PropertyGroup), as well as the reference packages that are going to be used by your app:

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

<PropertyGroup>
<OutputType>Library</OutputType>
<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.0.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.1.23557.2" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0-alpha.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0-alpha.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.6.0-beta.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.6.0-beta.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.6.0-beta.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.5.1" />
</ItemGroup>

</Project>

On the Extensions.cs file, you can find some extension methods such as:

  • AddServiceDefaults: used to add defaults functionality.
  • ConfigureOpenTelemetry: used to configure OpenTelemetry metrics and tracing
  • AddDefaultHealthChecks: adds default health checks endpoints
  • MapDefaultEndpoints: maps the health check endpoint to /health and the liveness endpoint to alive.

A remark: the Microsoft statement for the ServiceDefault project is that this project is specifically designed for sharing the Extensions.cs file and its functionality, and if you want to have some shared functionality or models, it’s recommended to use a conventional shared class library project for it.

For the ApiService and the Web projects, I will not go into details as they are common Web API and Blazor projects. Note that in both projects there is a call to the AddServiceDefaults method on the Program.cs file.

[Extra] Adding a new project to Aspire

In case you have a new project in the solution and you want to manually add Aspire support to the project, you can right-click on the project > Add > And select the option “.NET Aspire Orchestrator Support…”. For example:

In this example, the project was already added previously. But in case of a new project, this would include the project in the Program.cs file on the AppHost project:

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

And it would also include a reference in the csproj:

<ProjectReference Include="..\DotNetAspireDemo.ApiService\DotNetAspireDemo.ApiService.csproj" />

Dashboard

Start the project with DotNetAspire.Demo.AppHost as the Starter project (this project knows how to run the whole distributed application), and the following page will be opened in your browser with this nice dashboard:

In this dashboard, you can monitor various parts of your app, such as:

  • Projects: Displays information about your .NET projects in your .NET Aspire app, such as the app state, endpoints, environment variables.
  • Containers: Displays information about your app containers, such as state, image tag and port number (you should also see the Redis container you added for output caching with the name you provided.
  • Executables: Displays the running executables used by your app.
  • Logs: In this section, you can see the output logs for the projects, containers, executables, and can also see the logs in a table format (structured).
  • Traces: displays the traces for your application, providing information about the requests.
  • Metrics: Displays various instruments and meters that are exposed and their corresponding dimensions for your app.

If you click on the endpoints (on the projects page) you can also see the apiservice and the webfrontend running:

apiservice
webfrontend

Note that if you go to the Weather page, you will see some data coming from the API, and this data will be cached. You can refresh the page and the data will remain the same, for 5 seconds, which is the time that Redis is configured. But after this period new data will be displayed:

Refreshing after 5 seconds will retrieve different data (this config is found on the Weather.Razor file, in the @attribute [OutputCache(Duration = 5)]:

In the dashboard, you can also see your containers and executables. For example, the cache container that is now running:

After making some requests to the API and the FE, we can also see the logs in the dashboard:

The Traces for your APIs and Web projects:

Can also see a range of metrics for your APIs and FE, and also apply filters:

Conclusion

.NET Aspire is a stack that provides a range of important features such as resilience, service discovery, telemetry, health checks, facilitating the development of cloud-native apps easier and also integrates a dashboard where you can monitor your distributed applications.

This is the link for the project in GitHub: https://github.com/henriquesd/dotnetaspiredemo

If you like this demo, I kindly ask you to give a ⭐️ in the repository.

Thanks for reading!

--

--