Skip to content

WithLaunchSettings() extension to override or create a launch profile #9890

@frankhaugen

Description

@frankhaugen

Disclaimer: AI was used to compose a good coherent proposal with sources from code examples and the general idea description for the API.

Background and Motivation

This is intended to solve a simple problem: launchSettings.json are often "optimized" for working with a single app/microservice in isolation, and so having the ability to override or set the settings for each project in a type-safe manner would allow a much better developer experience.

Launch profiles drive Aspire’s local orchestration: they decide URLs, env-vars, debug flags, and whether dotnet watch is used ([learn.microsoft.com]1).
Today the only API surface is launchProfileName on AddProject, which merely picks a profile and copies a handful of fields ([learn.microsoft.com]2). Features such as hot-reload, alternate executables, or ad-hoc env-vars still require hand-editing JSON, breaking Aspire’s “configure everything in code” ethos ([learn.microsoft.com]3, [learn.microsoft.com]4).

A fluent WithLaunchSettings delegate mirrors existing builder hooks like WithEndpoint, WithEnvironment, WithArgs, and WithReplicas ([apisof.net]5, [learn.microsoft.com]6, [learn.microsoft.com]7, [learn.microsoft.com]8, [learn.microsoft.com]9) while letting developers compose or mutate launch properties programmatically.


Proposed API

file-scoped namespace Aspire.Hosting;

public static class ResourceBuilderLaunchSettingsExtensions
{
+   /// <summary>Mutate or create the effective launch profile for a project resource.</summary>
+   public static IResourceBuilder<ProjectResource> WithLaunchSettings(
+       this IResourceBuilder<ProjectResource> builder,
+       Action<LaunchProfileBuilder> configure);
}

public sealed class LaunchProfileBuilder
{
    // Fluent modifiers – return 'this' for chaining
    public LaunchProfileBuilder Profile(string name);
    public LaunchProfileBuilder ApplicationUrl(string url);
    public LaunchProfileBuilder CommandLineArgs(string args);
    public LaunchProfileBuilder ExecutablePath(string path);
    public LaunchProfileBuilder WorkingDirectory(string path);
    public LaunchProfileBuilder Environment(string key, string value);
    public LaunchProfileBuilder DotNetArguments(string args);
    // Additional members map 1-to-1 with launchSettings.json when needed
}
  • Restriction to ProjectResource ensures compile-time safety; launch settings are a .NET-specific construct ([learn.microsoft.com]10).
  • Implementation simply appends a LaunchProfileAnnotation to the in-memory model, similar to how WithEndpoint or WithEnvironment append their annotations ([apisof.net]5, [learn.microsoft.com]6).

Usage Examples

var api = builder
    .AddProject<Projects.WebApi>("api")
    .WithLaunchSettings(ls => ls
        .Profile("https")
        .ExecutablePath("dotnet")
        .CommandLineArgs("watch run")
        .ApplicationUrl("https://localhost:5050")
        .Environment("ASPNETCORE_ENVIRONMENT", "Development"));

var frontend = builder
    .AddProject<Projects.Frontend>("frontend")
    .WithLaunchSettings(ls => ls
        .Profile("dev")
        .CommandLineArgs("npm run dev")
        .WorkingDirectory("../frontend")
        .Environment("PORT", "0"))     // Aspire patches actual port
    .WaitFor(api)
    .WithReference(api);

The pattern looks and feels identical to other Aspire builder helpers, so existing muscle memory applies ([learn.microsoft.com]7, [learn.microsoft.com]9).


Alternative Designs

Option Why Rejected
Parameter-only WithLaunchSettings(string profile) Duplicates launchProfileName; still forces JSON edits for everything else.
Raw-JSON overload Loses IntelliSense and static validation; violates SRP.
MSBuild property injection Requires rebuilds for every tweak, slowing the inner loop Aspire optimises ([learn.microsoft.com]1).

Implementation sketch

The extension should mimic the existing annotation pattern used by Aspire’s other fluent helpers. Inside WithLaunchSettings you:

  1. Call builder.GetOrAddAnnotation<LaunchProfileAnnotation>() (same helper used by WithEndpoint to create/update endpoint metadata) to obtain the current launch-profile payload or create a new one. (learn.microsoft.com)
  2. Wrap that payload in a lightweight LaunchProfileBuilder that simply forwards each fluent call (ApplicationUrl, Environment, …) to set properties on the underlying annotation object, mirroring the fields documented for launch profiles. (learn.microsoft.com)
  3. Invoke the user-supplied delegate so callers can chain mutations, then return the original IResourceBuilder<ProjectResource> for further DSL composition. This parallels the design of WithEnvironment, ensuring deferred execution until the app-model is serialized. (learn.microsoft.com)
  4. At build time the orchestrator already reads LaunchProfileAnnotation when it wires up process start-info, so no additional runtime code is needed—the new annotation just slots into that pipeline. (learn.microsoft.com)

Because the method is an extension on IResourceBuilder<ProjectResource> it compiles for every project resource yet stays invisible to containers, in exactly the same way that WithReplicas scopes itself. (learn.microsoft.com)


Risks

  • API surface creep – adds a new builder type, but aligns with existing extension-method pattern ([apisof.net]5).
  • Conflict resolution – if both launchProfileName and WithLaunchSettings modify the same project, delegate wins; docs must state precedence clearly ([learn.microsoft.com]1).
  • Tooling parity – Visual Studio templates still emit JSON; guidance must note that code overrides file values at runtime ([learn.microsoft.com]4).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-app-modelIssues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions