An introduction to .NET Aspire

I was very excited to see the announcement of .NET Aspire, which shares many of the underlying philosophies of CADD. In this blog post, I will dive deeper into what .NET Aspire is, and what the preview version provides today.

Niclas Benjamin Tollstorff
Software Engineer

At cVation we are passionate about cloud development, and over the last 10 years we have channeled our experience around cloud development into our Cloud Accelerated Development & Delivery (CADD) toolbox. CADD ensures best practices, quality, and performance when we develop for the cloud, and it enables us to focus on the business application early in the development phase.

Our experience building CADD makes me very excited to see the announcement of .NET Aspire, which shares many of the underlying philosophies. In this blog post, I will dive deeper into what .NET Aspire is, and what the preview version provides today.

.NET Aspire

In Microsoft’s own words .NET Aspire is “an opinionated, cloud ready stack for building observable, production ready, distributed applications”. The intention is to simplify and streamline the way distributed applications are created and configured, while at the same time providing capabilities one would expect from a modern distributed architecture, such as logging, service discovery, and health checks. To see some of this in action, I will dive into Microsoft’s .NET Aspire Sample.

.NET Aspire Distributed App Host

The sample contains two applications:

  • An ASP.NET API with a single endpoint to produce weather data.

  • A frontend to consume and show the weather data.

These applications have a dependency between them because the frontend needs to invoke the API to retrieve the data. Additionally, this sample is configured to use Redis to cache the API data instead of asking each time. So how do these applications know about each other and the cache? And how does .NET Aspire know what to deploy?

The answer lies in what .NET Aspire calls the distributed app host. This is a .NET project responsible for defining the overall architecture including resources and references between them.

In the above image the app host defines that there are three resources in total: Two .NET projects and a Redis component, with the frontend referencing the other two. This definition, also known as the app model, makes it clear which resources need to talk to each other. When .NET Aspire is deployed to a cloud environment, the needed components are provisioned and any needed connection strings and service bindings are seamlessly injected into the resources that need them.

Also notice how Redis is added by simply calling a method on the builder with no need to handle credentials or connection strings. This Redis method is provided by a NuGet package and Redis is one of many .NET Aspire resources available today. Adding the Redis resource makes it a part of the deployed environment and available to the frontend project, but to make use of the cache within the frontend project a .NET Aspire Component is referenced within the frontend project itself.

.NET Aspire Components

A .NET Aspire component is a NuGet package which can be referenced by the individual applications within a .NET Aspire project to streamline usage and configuration of common cloud services.

In this example, the Redis component is referenced within the frontend project using the same resource identifier as defined in the app model. This pattern of specifying the resource identifier is useful in situations where a single app model uses multiple instances of the same cloud service, e.g. multiple cache instances.

Adding a .NET Aspire component enhances the application in several ways:

• It registers relevant services, such as SDK clients, using Dependency Injection. If the application needs multiple instances, it is possible to use .NET 8 Keyed Services.

• It configures logging, tracing, and telemetry automatically.

• It adds several types of health checks to check whether the app is running and whether it is healthy.

• It configures resiliency such as timeouts and retries.

• It provides a consistent configuration format for configuring the component, e.g. whether tracing is enabled. Cross-application configuration can also be defined centrally using Service Defaults.

.NET Aspire Local Development

Launching .NET Aspire locally will start all of the applications and launch the .NET Aspire dashboard. Note that this dashboard is only available for local development and is considered a tool to assist development.

As seen in the dashboard, there are three resources running: A Redis cache inside a Docker container, and two .NET projects.

The dashboard also provides a lot of other useful information, such as access to relevant URLs, logs, and environment variables, and it can visualize the trace logging and thus provide better insights into the communication between applications.

.NET Aspire Deployment

.NET Aspire is built on top of cloud-agnostic technologies and does not dictate a deployment target. For custom deployment targets, it is possible to generate a manifest file which describes the app model in detail and use this file as a foundation for deployments.

However, .NET Aspire currently has support for deployment to Azure Container Apps using the Azure Developer CLI, and they “expect the number of environments Aspire can deploy to to grow over time”.

The following image shows the Azure resources created by this .NET Aspire sample.

Final thoughts

In summary, .NET Aspire enables developers to build distributed applications in multiple ways. The clear and concise app model along with the component packages makes it straightforward to define resources and references, and the streamlined approach to configuration simplifies adding new services. The local developer experience is improved by the dashboard, and provisioning an environment based on Azure Container Apps is very easy.

However, it should also be mentioned that my experience with the CADD toolbox highlights some areas that could be improved even further in future versions of .NET Aspire:

In the CADD equivalent of .NET Aspire components, each component is accompanied by component-specific testing utilities. These utilities are useful in both regular in-memory tests as well as browser-based frontend testing. .NET Aspire does not provide a similar concept out of the box, and the documentation does not cover testing strategies currently.

Each project and environment can have different needs and the CADD components provide configuration options for the deployed cloud resources, such as being able to reduce costs by choosing different SKUs. It seems that .NET Aspire component configuration does not offer such options. However, due to the manifest file and being able to retrieve Bicep files using the Azure Developer CLI, it is possible to make manual changes to the deployed environment.

One of the benefits for development teams when building a distributed application is the ability to build, test, and deploy applications separately. CADD can generate pipelines with distinct jobs for each individual application and can identify what needs to run automatically based on file changes. Although .NET Aspire documents an example pipeline using the Azure Developer CLI, it does not mention if such deployment patterns are possible.

I am very excited to see what the future will bring for .NET Aspire.

Do you need help getting started with the analysis and clarification?

Reach out and let us help you control your Azure Cost.

Get in touch