.NET Tools How-To's

JetBrains Rider and the .NET Aspire Plugin

With the .NET 8 announcement, developers were surprised to learn of a new opinionated, cloud-ready stack for building observable distributed applications. Of course, we’re talking about .NET Aspire, a solution of tools and patterns delivered to developers via the NuGet workloads found in newer versions of the .NET SDK, similar to Multi-Application UI or MAUI.

In this post, we’ll cover what .NET Aspire is, who it is for, and what JetBrains is doing to support this new approach to developing distributed applications on the .NET platform. Also, for all you internals geeks, we have a few details about the inner workings of our JetBrains Rider plugin.

What is .NET Aspire?

It’s not a secret that the world of .NET is dominated by enterprise applications, with many solutions spanning multiple servers. Distributing an application can provide many benefits for scaling solutions to complex problems but can also introduce several maintenance burdens when misdesigned.

.NET Aspire provides a recommended set of tools and patterns that aid developers in building distributed applications. The project starts by offering three foundational pillars to building a solution: Orchestration, Components, and Tooling.

Production usually has your application running, backed by supporting services like a database. Or it looks like Kubernetes, with several microservices talking with each other and with supporting services like databases or caches. Regardless of your production environment’s complexity, even a web application backed by a database needs additional effort to get going on your development machine. Wouldn’t doing a git clone, compiling, and starting your application to get a working development environment be so much nicer? .NET Aspire is designed to solve these problems.

Orchestration is the ability for multiple projects within your solution to organize and culminate into a single solution. .NET Aspire tackles orchestration by making it part of the codebase with C# code that defines the overall app composition along with providing developers mechanisms for service discovery within the defined architecture. These services are a mix of natively running applications and Docker containers.

The following snippet shows how to define a web application that depends on a Redis cache instance with .NET Aspire.

// Create a distributed application builder given the command line arguments.
var builder = DistributedApplication.CreateBuilder(args);

// Add a Redis server to the application.
var cache = builder.AddRedis("cache");

// Add the frontend project to the application and configure it to use the 
// Redis server, defined as a referenced dependency.
builder.AddProject<Projects.MyFrontend>("frontend")
       .WithReference(cache);

Orchestration and service discovery in .NET Aspire are simplified using logical names, such as frontend and cache, allowing developers to reason about architectural decisions.

Components are another essential part of .NET Aspire. Developers will typically build solutions that are a mixture of custom code and third-party solutions, such as databases, API services, and other distributed technologies. With .NET Aspire, Aspire developers write components to offer solutions quickly to users through registration. These components typically provide a set of defaults, helping users navigate a tricky set of options. The previous code registered the Redis component into the solution’s architecture. These steps include adding Redis dependencies into dependency injection containers, adding telemetry information, and allowing service discovery. A current collection of known .NET Aspire components exists, but this model can support any solution element.

The final but crucial element of .NET Aspire is tooling. The .NET Aspire workload delivers a template for developers as a reference to start building their solutions.

JetBrains Rider New Solution screen showing .NET Aspire workload

These templates include a set of service defaults that help developers use features such as OpenTelemetry, health check endpoints, and service discovery elements when using HttpClient instances. .NET Aspire also includes a built-in dashboard that provides a UI for current services, environment variables, and OpenTelemetry visualizations.

.NET Aspire Dashboard in web client

These are great additional features for .NET Aspire. That said, JetBrains is a tooling vendor and a developer of IDEs. We believe strongly in the Integrated part of our tools and the ability to access functionality without context switching between a browser and code.
So, let’s look at the JetBrains Rider .NET Aspire plugin and what it offers Rider users.

Getting started with the JetBrains Rider .NET Aspire plugin

The first step to working with .NET Aspire is installing the .NET 8 SDK, Docker for Desktop, and the .NET Aspire workloads. You’ll also need our JetBrains .NET Aspire plugin. In a new terminal session, type the following commands.

dotnet workload update
dotnet workload install aspire

Next, we recommend installing the JetBrains .NET Aspire plugin from the JetBrains Marketplace. With Marketplace selected in the Plugins section, search for .NET Aspire and install the plugin written by JetBrains developer Rival Abdrakhmanov.

.NET Aspire plugin in the Settings Plugins tab

Once installed, we can create a new .NET Aspire solution. The workload will install new templates visible within JetBrains Rider’s New Solution screen. We recommend starting with the “.NET Aspire Starter Application” to get an overview of what an Aspire solution looks like.

New Solution Dialog creating a new .NET Aspire solution.

Once you’ve created a solution, you’ll notice several projects in the Solution Explorer. The AppHost project is where orchestration occurs.

JetBrains Rider showing a new .NET Aspire solution in action.

You’ll also notice a new Aspire Host Run Configuration. Running this configuration will launch your distributed application. You’ll see all projects running, including the .NET Aspire dashboard.

.NET Aspire solution in Run tool window.

The new .NET Aspire node in the Services tool window is more valuable than the console output typically seen in the run window. Here, you can see the run configuration along with all the elements of your architecture. You can view vital information such as the application’s URL and environment variables and additionally see OpenTelemetry metrics as they stream from your solution.

.NET Aspire Plugin showing OpenTelemetry information in Services tool window.

Note that if you don’t see .NET Aspire in the services window or the OpenTelemetry data, you may need to enable the metrics collection option in the plugin’s settings page.

While still in the early stages of development, we’re also working on adding diagram support for architectures orchestrated in .NET Aspire. The diagramming feature uses the OpenTelemetry trace information to discover relationships in your architecture and diagram those nodes into an easy-to-follow visualization.

A flowchart diagram generated from OpenTelemetry traces.

As .NET Aspire’s offerings expand, we will continue to adapt and enhance the plugin, which is open source. You may also suggest new features or file bugs for any issues encountered on the GitHub repository for the .NET Aspire plugin for the JetBrains Rider repository page.
These are fantastic features integrated into JetBrains Rider, all thanks to the hard work of JetBrains Rider team member Rival Abdrakhmanov. Now would be a great time to hear from the developer as he explains some of the internal workings of the .NET Aspire plugin. Take it away, Rival.

The internals of the .NET Aspire plugin

Hello, I’m Rival, and I’d like to show you some internal communication mechanisms between our plugin and .NET Aspire and what happens under the hood when you run/debug an Aspire project in Rider.

.NET Aspire provides an exciting way to integrate with IDEs: it has an IDE execution mode. When you launch an Aspire host project with the dotnet run command, a special orchestrator, the Developer Control Plane (DCP), is responsible for starting all the dependent projects and containers. While the DCP is a capable orchestrator, it isn’t convenient when we want to debug the application. This limitation means that DCP has to delegate the launch task to the IDE, in our case, JetBrains Rider.

To do that, DCP can send HTTP requests and interact via Web Socket. Let’s experiment and create a simple application to control Aspire. We must implement three endpoints:

  •  PUT /run_session
  •  DELETE /run_session/<run session ID> 
  • GET /run_session/notify. 

DCP will use the first and second endpoints to send commands to our application to run and stop a particular project; via the third one, we will send some notifications to DCP. A straightforward implementation might look like the following.

app.UseWebSockets();
var group = app.MapGroup("/run_session");
group.MapPut("/", (Session session) =>
{
    return TypedResults.Created("/run_session/1");
});
group.MapDelete("/{sessionId:guid}", (Guid _) => TypedResults.Ok());
group.MapGet("/notify", async context =>
{
    if (context.WebSockets.IsWebSocketRequest)
    {
        using var ws = await context.WebSockets.AcceptWebSocketAsync();
        await Receive(ws);
    }
    else
    {
        context.Response.StatusCode = StatusCodes.Status400BadRequest;
    }
});

After that, we can start our application and put a breakpoint on the line with a PUT request. To switch the orchestrator to the IDE execution mode, we need to set two environment variables. The easiest way is to add "DEBUG_SESSION_PORT": "localhost:5063" and "DEBUG_SESSION_TOKEN": "token" to the launchSettings.json file for the Aspire host project. Run the launch setting with a dotnet run command, and the debugger will suspend the execution on our breakpoint. Here, you see how DCP asks your application to run one of the projects.

Debugging session in a .NET Aspire application.

To learn more, check out the .NET Aspire plugin source code. Additionally, you can find out how our plugin intercepts OpenTelemetry requests to show metric charts and trace diagrams. We’re open to contributions, so if you find that we could do better, do not hesitate to open an issue or a pull request.

Conclusion

.NET Aspire is a fascinating new way to tackle the complex issues around building distributed applications on the .NET stack. The opinionated approach means developers can focus more time on solving business problems while relying on the expertise and experience of Microsoft experts. As mentioned, .NET Aspire solves issues around orchestration, components, and tooling. With our work at JetBrains, we want to provide additional integrated tooling so you can avoid unnecessary context switching that can typically slow down even the fastest developers.

The .NET Aspire plugin is available now in the JetBrains Marketplace for the current release of JetBrains Rider. You can also get involved by visiting the GitHub repository to request new features and submit issues.

If you have any questions or ideas, leave them in the comments section below.

image description

Discover more