The demand for cloud-native applications has soared due to the impressive features offered by cloud services like high availability, scalability, durability, low maintenance, easy configurability, robust security, and efficient monitoring tools. Despite these advantages, designing a cloud-native application can be a complex and time-consuming task, requiring careful consideration of various factors. Developers often find themselves grappling with decisions about technologies, tools, stacks, and cloud services, leading to confusion and challenges in building future-proof cloud solutions.

Recognizing these struggles, developers have expressed the need for a more accessible solution. Building applications for the cloud has been perceived as too challenging, diverting developers from their primary focus on business logic. The good news is that there’s an incredible solution to simplify cloud-ready application development and solution design – introducing .NET Aspire.

.NET Aspire is a user-friendly solution designed for crafting applications ready for the cloud. It simplifies the development process, allowing developers to focus on their core business logic. Packaged as a set of NuGet packages, .NET Aspire handles specific cloud-related concerns. Whether you’re dealing with databases, messaging, or caching, .NET Aspire has you covered. It’s your go-to stack for creating cloud-ready, observable, and production-ready applications.

Cloud-native applications often need to connect to diverse services, including databases, storage and caching solutions, messaging providers, and other web services. .NET Aspire is crafted to simplify the process of establishing connections and configurations across these services.

In this article, we will learn about .NET Aspire, getting started and develop a .NET Aspire app using Visual Studio.

.NET Aspire is available in preview with .NET and will be release generally as part of .NET 8 next year.

Getting Started with .NET Aspire

In this section, we create a project with .NET Aspire. let’s commence.

Prerequisites

Alternatively, we can use Visual Studio Code as IDE.

We will Install the Preview Version of Visual Studio 2022.

And then, we will install .NET Aspire workload from command as shown below:

dotnet workload install aspire

We need to restart the machine to get the .NET Aspire project templates. Let’s open the Visual Studio Preview after restart.

We will select .NET Aspire Starter Application from the project template.

We will input solution name as MyFirstAspireApp

Select .NET 8.0 LTS, and also check use Redis for caching (requires Docker) option.

Our .NET Aspire project is created successfully. Let’s explore the projects and structure of the solution.

Solution Folder Structure:

└───📂 MyFirstAspireApp
     ├───📂 MyFirstAspireApp.ApiService
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── MyFirstAspireApp.ApiService.csproj
     │    └─── Program.cs
     ├───📂 MyFirstAspireApp.AppHost
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── MyFirstAspireApp.AppHost.csproj
     │    └─── Program.cs
     ├───📂 MyFirstAspireApp.ServiceDefaults
     │    ├─── MyFirstAspireApp.ServiceDefaults.csproj
     │    └─── Extensions.cs
     ├───📂 MyFirstAspireApp.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
     │    ├─── MyFirstAspireApp.Web.csproj
     │    ├─── Program.cs
     │    └─── WeatherApiClient.cs
     └─── MyFirstAspireApp.sln

.NET Aspire project template will create 4 projects.

ProjectName.ApiService: This ASP.NET Core Minimal API project serves as the data provider for the frontend. It relies on the shared ProjectName.ServiceDefaults project.

ProjectName.AppHost: As the orchestrator project, it is designed to interconnect and configure various projects and services within your application. Set this orchestrator as the Startup project, and it has dependencies on both ProjectName.ApiService and ProjectName.Web projects.

ProjectName.ServiceDefaults: A shared .NET Aspire project responsible for managing configurations reused across your solution’s projects. It handles aspects related to resilience, service discovery, and telemetry.

ProjectName.Web: This ASP.NET Core Blazor App project comes with default .NET Aspire service configurations and depends on the ProjectName.ServiceDefaults project.

Starter .NET Aspire Project

We can delve further into the Aspire Solution. The two projects AppHost and ServiceDefaults plays vital role for .NET Aspire, and it set to True for IsAspireHost and IsAspireSharedProject.

The ProjectName.Web project is a conventional ASP.NET Core Blazor App that delivers the frontend user interface. On the other hand, the ProjectName.ApiService project follows the standard ASP.NET Core Minimal API template structure. Both projects rely on the shared ProjectName.ServiceDefaults project, which centrally manages configurations reused across various projects in your solution.

Let’s explore AppHost project’s program.cs file.

var builder = DistributedApplication.CreateBuilder(args);

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

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

builder.AddProject<Projects.MyFirstAspireApp_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.Build().Run();

The code creates an IDistributedApplicationBuilder using DistributedApplication.CreateBuilder(), then adds a Redis container named “cache” using AddRedisContainer, storing the result in a variable named cache of type IResourceBuilder<RedisContainerResource>. It adds the ProjectName.ApiService project to the application model using AddProject, configuring service discovery and communication. Another AddProject call adds the ProjectName.Web project, and multiple WithReference calls pass the cache and apiservice variables. The app is then built and run using DistributedApplication.Run(), launching the app and its dependencies.

Similarly, The service defaults project provides an extension method called AddServiceDefaults on the IHostApplicationBuilder type. It serves as a baseline, and you have the flexibility to customize it according to your specific requirements.

We will explore the frontend project’s program.cs:

using MyFirstAspireApp.Web;
using MyFirstAspireApp.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=> client.BaseAddress = new("http://apiservice"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
}

app.UseStaticFiles();

app.UseAntiforgery();

app.UseOutputCache();

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

app.Run();

The code initiates the configuration process by invoking AddServiceDefaults, establishing the shared defaults for the application. Subsequently, AddRedisOutputCache is called, using the identical connectionName employed when integrating the Redis container “cache” into the application model. This configuration sets up the app to utilize Redis for output caching. Lastly, AddHttpClient is invoked, setting up the HttpClient.BaseAddress as “http://apiservice.” This name corresponds to the one used during the addition of the API project to the application model. With service discovery in place, it automatically resolves to the correct address for the API project.

Let’s run the project :). Ensure that AppHost project is set as startup project and Docker is running.

You will see the running application as shown:

We will open the URL as shown in above console: http://localhost:15073/

This is the URL of dashboard of .NET Aspire.

The dashboard will show two running applications: aspservice and webfrontend.

The dashboard is quite comprehensive with several options like Projects, containers, executables, logs traces and metrics.

You can check containers, endpoints of the applications, view environments details, logs and many more. This is one of the amazing features of .NET Aspire. Let’s open my webfrontend application.

Congratulations!! We can run successfully the .NET Aspire application.

Furthermore, you can see in our desktop docker that a docker image is created and container is running.

This is how we can create .NET Aspire, a cloud ready solution for building observable production ready, distributed applications.

Conclusion

In conclusion, .NET Aspire emerges as a powerful solution for simplifying cloud-ready application development and solution design. With its opinionated, cloud-native stack, it streamlines the complexities associated with building observable, production-ready, and distributed applications. By providing a set of NuGet packages that handle specific cloud-native concerns, .NET Aspire facilitates the creation of microservices-based applications, promoting high availability, scalability, and durability. The framework addresses the challenges developers face in choosing technologies, tools, stacks, and cloud services, offering a cohesive approach to future-proof cloud solutions.

.NET Aspire’s modular architecture, as demonstrated in the provided examples, allows developers to easily connect and configure different projects and services within their applications. Whether it’s handling service defaults, incorporating Redis for output caching, or configuring HttpClient for seamless communication between projects, .NET Aspire streamlines the development process. Embracing .NET Aspire not only simplifies the intricacies of cloud-native application development but also establishes a foundation for building robust, maintainable, and scalable solutions in the ever-evolving landscape of software development.