Skip to content

Add optional parameters and programmatic value setting#18108

Draft
davidfowl wants to merge 1 commit into
mainfrom
davidfowl/optional-parameter-resources
Draft

Add optional parameters and programmatic value setting#18108
davidfowl wants to merge 1 commit into
mainfrom
davidfowl/optional-parameter-resources

Conversation

@davidfowl

@davidfowl davidfowl commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Description

Parameters currently do not have a complete first-class model for two related scenarios: optional parameters that can intentionally have no value, and programmatic parameter values that can be set by AppHost code instead of only through configuration, prompts, or generated defaults. This change adds both pieces so run, publish, dashboard, C# AppHost, and TypeScript AppHost flows share the same parameter value contract.

Users can now mark parameter resources optional with WithOptional() / WithRequired(...), allowing unset optional parameters to resolve to null without prompting or blocking publish. Users can also inspect and set parameter values programmatically with TryGetCurrentValue(...) and SetValueAsync(...); observed parameter updates synchronize dashboard state, resolve missing-value prompts, and preserve required-parameter validation when a required parameter is cleared.

The implementation centralizes value state on ParameterResource and refactors ParameterProcessor to consume that public/resource-level contract instead of manipulating internal completion state directly. ATS and TypeScript generation expose the value-management APIs, including nullable value metadata for setValueAsync and tryGetCurrentValue.

User-facing usage

C# AppHost:

var messagePrefix = builder.AddParameter("message-prefix")
                           .WithOptional();

if (!messagePrefix.Resource.TryGetCurrentValue(out _))
{
    await messagePrefix.Resource.SetValueAsync("local");
}

TypeScript AppHost:

const messagePrefix = await builder.addParameter("message-prefix").withOptional();
if (await messagePrefix.tryGetCurrentValue() === null) {
    await messagePrefix.setValueAsync({ value: "local" });
}

The TypeScript playground now includes this pattern and passes the parameter to the API as MESSAGE_PREFIX.

Validation

  • ./build.sh --build /p:SkipNativeBuild=true /p:RepositoryCommit=$(git rev-parse HEAD)
  • dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.AddParameterTests" --filter-class "*.ParameterProcessorTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • git diff --check

Fixes #17710

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Copilot AI review requested due to automatic review settings June 11, 2026 04:40
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18108

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18108"

@davidfowl davidfowl changed the title Add optional parameter value management Add optional parameters and programmatic value setting Jun 11, 2026
@davidfowl davidfowl force-pushed the davidfowl/optional-parameter-resources branch from 663b2d2 to 564a208 Compare June 11, 2026 04:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds first-class support for optional parameter resources and programmatic parameter value management in Aspire. It introduces a Required property on ParameterResource (defaulting to true), fluent builder extensions (WithOptional(), WithRequired()), and a suite of value-management methods (TrySetValue, TrySetException, SetValueAsync, SetExceptionAsync, TryGetCurrentValue). The ParameterProcessor is refactored to use an observer pattern via ValueChanged events instead of directly manipulating internal TaskCompletionSource state, enabling dashboard state synchronization when values are set programmatically. ATS code generation is updated to propagate nullability metadata for parameters and return types.

Changes:

  • New ParameterResource public API for optional parameters and programmatic value management, with an internal event-based observer pattern consumed by ParameterProcessor for dashboard state synchronization.
  • ParameterProcessor refactored to use thread-safe collections with locks, observer-based state updates, and a new SetValueAsync method for non-interactive value setting.
  • ATS capability scanner updated to detect and propagate C# nullability annotations for method parameters and return types, reflected in TypeScript codegen snapshots.
Show a summary per file
File Description
src/Aspire.Hosting/ApplicationModel/ParameterResource.cs Core changes: Required property, TryGetCurrentValue, TrySetValue, TrySetException, SetValueAsync, SetExceptionAsync, ValueChanged event, ParameterResourceValueChangedEventArgs
src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs Refactored to observer pattern: ObserveParameterValueChanges, thread-safe unresolved parameters, SetValueAsync for non-interactive value setting, SetValueCoreAsync
src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs New WithOptional() and WithRequired(bool) fluent extension methods
src/Aspire.Hosting/Publishing/ManifestPublishingContext.cs Emits "required": false in manifest for optional parameters
src/Aspire.Hosting.RemoteHost/AtsCapabilityScanner.cs Adds NullabilityInfoContext-based nullability detection for parameters and return types, GetEffectiveNullability to unwrap Task<T?>/ValueTask<T?>
playground/TypeScriptAppHost/apphost.mts Playground example using addParameter().withOptional() and tryGetCurrentValue/setValueAsync
tests/Aspire.Hosting.Tests/AddParameterTests.cs Tests for optional parameters, TrySetValue, SetValueAsync, TrySetException, SetExceptionAsync, manifest output
tests/Aspire.Hosting.Tests/Orchestrator/ParameterProcessorTests.cs Tests for processor integration: optional init, programmatic set, dashboard state sync, delete optional
tests/Aspire.Hosting.Tests/ExecutionConfigurationGathererTests.cs Test verifying unset optional parameter env vars are omitted
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs Tests for ATS scanner detecting nullable parameter/return types
tests/.../Snapshots/WithOptionalStringCapability.verified.txt Updated snapshot with IsNullable: true
tests/.../Snapshots/AddTestRedisCapability.verified.txt Updated snapshot with IsNullable: true
tests/.../Snapshots/WithDataVolumeOptionsMerged.verified.ts TypeScript type updated to string | null
tests/.../Snapshots/AtsGeneratedAspire.verified.ts Full generated TypeScript updated with | null for nullable optional params

Copilot's findings

  • Files reviewed: 14/15 changed files
  • Comments generated: 1

Comment thread src/Aspire.Hosting/ApplicationModel/ParameterResource.cs
Copilot AI review requested due to automatic review settings June 11, 2026 05:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 14/15 changed files
  • Comments generated: 0 new

Make Aspire parameters first-class for both optionality and runtime value
management, and surface the new capabilities to polyglot hosts.

Optional parameters:
- WithOptional() marks a parameter resource as optional so a missing value
  no longer prompts or fails resolution; WithRequired() restores the default
  required behavior. Custom-input parameters honor the Required flag.

Programmatic value management:
- ParameterResource.SetValueAsync / TryGetCurrentValue let code set and read a
  parameter's value at runtime.
- ParameterProcessor.SetValueAsync is the persistence-aware path: with
  saveToUserSecrets it writes the value back to user secrets, and setting a
  value notifies observers so the dashboard reflects the change live.

Polyglot exposure:
- The new methods are exported via ATS ([AspireExport]) so TypeScript, Java,
  Python, Go, and Rust app hosts get them. The generated bindings are additive
  only - no changes to the existing generated surface. Reference-type
  nullability is intentionally not propagated into the model, preserving the
  established convention (for example getConnectionString() stays non-null).

Publish vs run:
- ManifestPublishingContext handles optional parameters when publishing.

Includes unit tests for AddParameter optionality, ParameterProcessor value
management and notification, execution configuration gathering, and the ATS
scanner capability for the new parameter methods.

Related: #17710

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl force-pushed the davidfowl/optional-parameter-resources branch from 50c76c3 to 3d326cb Compare June 11, 2026 15:06
Copilot AI review requested due to automatic review settings June 11, 2026 15:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new

@github-actions

Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 114 passed, 0 failed, 2 unknown (commit 3d326cb)

View all recordings
- Test Detail
AddPackageInteractiveWhileAppHostRunningDetached Recording · Job · CLI logs
AddPackageWhileAppHostRunningDetached Recording · Job · CLI logs
AgentCommands_AllHelpOutputs_AreCorrect Recording · Job · CLI logs
AgentInitCommand_DefaultSelection_InstallsDefaultSkills Recording · Job · CLI logs
AgentInitCommand_MigratesDeprecatedConfig Recording · Job · CLI logs
AgentInit_NonInteractive_BundleOnlySkillsNotInCatalog Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated Recording · Job · CLI logs
AllPublishMethodsBuildDockerImages Recording · Job · CLI logs
AspireAddAndStartWorkAgainstLegacyAppHostTs Recording · Job · CLI logs
AspireAddPackageVersionToDirectoryPackagesProps Recording · Job · CLI logs
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost Recording · Job · CLI logs
AspireInit_ExistingAppHostDir_RecreatesNuGetConfigKeepsFiles Recording · Job · CLI logs
AspireInit_SolutionFile_BuildsAgainstChannelHive Recording · Job · CLI logs
AspireStartUpdatesStaleTypeScriptAppHostPath Recording · Job · CLI logs
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps Recording · Job · CLI logs
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent Recording · Job · CLI logs
Banner_DisplayedOnFirstRun Recording · Job · CLI logs
Banner_DisplayedWithExplicitFlag Recording · Job · CLI logs
Banner_NotDisplayedWithNoLogoFlag Recording · Job · CLI logs
CertificatesClean_RemovesCertificates Recording · Job · CLI logs
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate Recording · Job · CLI logs
CertificatesTrust_WithUntrustedCert_TrustsCertificate Recording · Job · CLI logs
ConfigSetGet_CreatesNestedJsonFormat Recording · Job · CLI logs
CreateAndRunAspireStarterProject Recording · Job · CLI logs
CreateAndRunAspireStarterProjectWithBundle Recording · Job · CLI logs
CreateAndRunEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJavaEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJsReactProject Recording · Job · CLI logs
CreateAndRunPolyglotAppHostWithDevLocalhostUrls Recording · Job · CLI logs
CreateAndRunPythonReactProject Recording · Job · CLI logs
CreateAndRunTypeScriptEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunTypeScriptStarterProject Recording · Job · CLI logs
CreateJavaAppHostWithViteApp Recording · Job · CLI logs
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DeployK8sBasicApiService Recording · Job · CLI logs
DeployK8sWithExternalHelmChart Recording · Job · CLI logs
DeployK8sWithGarnet Recording · Job · CLI logs
DeployK8sWithMongoDB Recording · Job · CLI logs
DeployK8sWithMySql Recording · Job · CLI logs
DeployK8sWithPostgres Recording · Job · CLI logs
DeployK8sWithRabbitMQ Recording · Job · CLI logs
DeployK8sWithRedis Recording · Job · CLI logs
DeployK8sWithSqlServer Recording · Job · CLI logs
DeployK8sWithValkey Recording · Job · CLI logs
DeployTypeScriptAppToKubernetes Recording · Job · CLI logs
DescribeCommandResolvesReplicaNames Recording · Job · CLI logs
DescribeCommandShowsRunningResources Recording · Job · CLI logs
DetachFormatJsonProducesValidJson Recording · Job · CLI logs
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance Recording · Job · CLI logs
DoPublishAndDeployListStepsWork Recording · Job · CLI logs
DocsCommand_RendersInteractiveMarkdownFromLocalSource Recording · Job · CLI logs
DoctorCommand_DetectsDeprecatedAgentConfig Recording · Job · CLI logs
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain Recording · Job · CLI logs
DoctorCommand_WithSslCertDir_ShowsTrusted Recording · Job · CLI logs
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted Recording · Job · CLI logs
DotNetRunFileBasedAppHostUsesAspireCliBundle Recording · Job · CLI logs
DotNetRunProjectAppHostUsesAspireCliBundle Recording · Job · CLI logs
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain Recording · Job · CLI logs
GlobalMigration_HandlesCommentsAndTrailingCommas Recording · Job · CLI logs
GlobalMigration_HandlesMalformedLegacyJson Recording · Job · CLI logs
GlobalMigration_PreservesAllValueTypes Recording · Job · CLI logs
GlobalMigration_SkipsWhenNewConfigExists Recording · Job · CLI logs
GlobalSettings_MigratedFromLegacyFormat Recording · Job · CLI logs
IngressWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory Recording · Job · CLI logs
InteractiveCSharpInitCreatesExpectedFiles Recording · Job · CLI logs
InvalidAppHostPathWithComments_IsHealedOnRun Recording · Job · CLI logs
JavaScriptHostingApisRunFromTypeScriptAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelTypeScriptAppHost Recording · Job · CLI logs
LegacySettingsMigration_AdjustsRelativeAppHostPath Recording · Job · CLI logs
LogsCommandShowsResourceLogs Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterApp Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterAppIsolated Recording · Job · CLI logs
ProcessCommandCallbackReceivesCliArguments Recording · Job · CLI logs
PsCommandListsRunningAppHost Recording · Job · CLI logs
PsFormatJsonOutputsOnlyJsonToStdout Recording · Job · CLI logs
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts Recording · Job · CLI logs
PublishWithConfigureEnvFileUpdatesEnvOutput Recording · Job · CLI logs
PublishWithDockerComposeServiceCallbackSucceeds Recording · Job · CLI logs
PublishWithoutOutputPathUsesAppHostDirectoryDefault Recording · Job · CLI logs
ResourceCommand_FailedExec_ShowsLogPathAndLogHasEntries Recording · Job · CLI logs
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput Recording · Job · CLI logs
RestoreGeneratesSdkFiles Recording · Job · CLI logs
RestoreGeneratesSdkFiles_WithConfiguredToolchain Recording · Job · CLI logs
RestoreRefreshesGeneratedSdkAfterAddingIntegration Recording · Job · CLI logs
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes Recording · Job · CLI logs
RunFromParentDirectory_UsesExistingConfigNearAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
SecretCrudOnDotNetAppHost Recording · Job · CLI logs
SecretCrudOnTypeScriptAppHost Recording · Job · CLI logs
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels Recording · Job · CLI logs
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets Recording · Job · CLI logs
StartReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
StartReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
StopAllAppHostsFromAppHostDirectory Recording · Job · CLI logs
StopJavaPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopNonInteractiveSingleAppHost Recording · Job · CLI logs
StopTypeScriptPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopWithNoRunningAppHostExitsSuccessfully Recording · Job · CLI logs
TerminalAttachFrontend_ShowsViteHelpAndDetaches Recording · Job · CLI logs
TypeScriptAppHostRunDoesNotDeadlockWhenLazyOptionsInvokeAsyncCallback Recording · Job · CLI logs
TypeScriptAppHostWithVite_AllowsDifferentGuestPkgManager Recording · Job · CLI logs
UnAwaitedChainsCompileWithAutoResolvePromises Recording · Job · CLI logs
UpdateToStable_CSharpEmptyAppHost_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_CSharpSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScriptSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScript_PreviewsStablePkgsAndKeepsChannel Recording · Job · CLI logs

📹 Recordings uploaded automatically from CI run #27356599605

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ParameterProcessor.SetValueAsync and ParameterResource.TryGetCurrentValue for programmatic parameter value management

2 participants