Description
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 howWithEndpoint
orWithEnvironment
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:
- Call
builder.GetOrAddAnnotation<LaunchProfileAnnotation>()
(same helper used byWithEndpoint
to create/update endpoint metadata) to obtain the current launch-profile payload or create a new one. (learn.microsoft.com) - 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) - Invoke the user-supplied delegate so callers can chain mutations, then return the original
IResourceBuilder<ProjectResource>
for further DSL composition. This parallels the design ofWithEnvironment
, ensuring deferred execution until the app-model is serialized. (learn.microsoft.com) - 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
andWithLaunchSettings
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).