-
Notifications
You must be signed in to change notification settings - Fork 852
Description
Summary
When an exported API uses a C# union type as a parameter, Aspire should infer the equivalent AspireUnion metadata automatically instead of requiring authors to write AspireUnion manually.
This is based on the preview C# union feature documented for the .NET 11 preview train.
Motivation
Today, authors can model an exported union-shaped parameter by writing something like:
[AspireExport(MethodName = "runAsExisting")]
internal static IResourceBuilder<DurableTaskSchedulerResource> RunAsExisting(
this IResourceBuilder<DurableTaskSchedulerResource> builder,
[AspireUnion(typeof(string), typeof(IResourceBuilder<ParameterResource>))]
object connection)That works, but it forces Aspire-specific metadata into APIs that could otherwise be expressed naturally in C#.
With C# unions, authors should be able to write a normal union parameter and have the exporter infer the same union shape.
Example
C# authoring model
public union ExistingConnection(string, IResourceBuilder<ParameterResource>);
[AspireExport(MethodName = "runAsExisting")]
internal static IResourceBuilder<DurableTaskSchedulerResource> RunAsExisting(
this IResourceBuilder<DurableTaskSchedulerResource> builder,
ExistingConnection connection)Aspire export model
The exporter should treat the parameter as if it had:
[AspireUnion(typeof(string), typeof(IResourceBuilder<ParameterResource>))]without requiring the user to write that attribute.
In other words, a parameter whose type is a C# union should be exported the same way as a parameter annotated with the equivalent AspireUnion case list.
Proposed behavior
- If an exported parameter type is a C# union type, infer its case types from the union declaration /
[Union]pattern. - Normalize that inferred information into the same internal representation currently used for
AspireUnion. - Continue to support explicit
AspireUnionfor non-C#-union scenarios and as a fallback/manual escape hatch.
Inference rule
For a union declaration:
union U(T1, T2, ... Tn)an exported parameter of type U should be treated as if it were annotated with:
[AspireUnion(typeof(T1), typeof(T2), ..., typeof(Tn))]while keeping the declared parameter type as U.
Notes
- The author-facing API does not need to be rewritten to
object. - Only the exporter/analyzer pipeline needs to understand the union shape.
- If we generate bridge/shim code internally, that lowering can erase to another representation, but the source signature should stay as the C# union type.
- This should work both for the
uniondeclaration syntax and for custom union types that follow the[System.Runtime.CompilerServices.Union]pattern.
Edge cases to consider
- Nullable case types
- Generic case types
- Nested unions
- Precedence/interaction if explicit
AspireUnionis also present - Diagnostics when a union type is malformed or not exportable
Expected outcome
Aspire-exported APIs can adopt C# unions naturally, and the existing AspireUnion machinery becomes the normalized implementation detail rather than something users must author directly.