Skip to content

Retire standalone C# unions article; distribute in-context#37276

Draft
danroth27 wants to merge 1 commit into
mainfrom
danroth27/unions-in-context
Draft

Retire standalone C# unions article; distribute in-context#37276
danroth27 wants to merge 1 commit into
mainfrom
danroth27/unions-in-context

Conversation

@danroth27

@danroth27 danroth27 commented Jun 21, 2026

Copy link
Copy Markdown
Member

Addresses #37275 (retire fundamentals/unions.md and integrate per-area) and #37274 (Blazor-specific notes in the relevant Blazor articles).

Why

C# unions are a C# language feature whose ASP.NET Core surface is small and flows entirely through existing serialization-driven features (JSON request/response body, JsonHubProtocol, Blazor JS interop, persistent component state, prerendered parameters, OpenAPI schema generation). A standalone article under Fundamentals overstates the concept — Fundamentals is for foundational building blocks like middleware, configuration, routing, DI — and forces readers to leave the article they were already in to find a one-paragraph note.

The standalone article also spent meaningful space documenting how System.Text.Json serializes unions (case selection, classifiers, [JsonUnion], ambiguity). That behavior is independent of ASP.NET Core and belongs in the STJ docs, not here. The in-context notes added in this PR link out to that STJ content for serialization details.

What this PR changes

Removals

  • aspnetcore/fundamentals/unions.md (326 lines)
  • ❌ TOC entry for "C# union types" under Fundamentals
  • ➕ Redirect to fundamentals/minimal-apis/responses (the most common starting point for union usage)

In-context additions — one short :::moniker range=">= aspnetcore-11.0" block per article:

Article What it covers
fundamentals/minimal-apis/responses.md Returning a union from a handler — only the active case is serialized
web-api/action-return-types.md Returning a union from a controller action; same rule for [FromBody]
signalr/hubs.md Hub method parameters/returns/stream items work with unions on JsonHubProtocol
fundamentals/openapi/aspnetcore-openapi.md Unions surface as anyOf schemas; case names reuse standalone component names
blazor/components/index.md [Parameter] of union type — Slot/Toast canonical example plus the Razor literal-attribute workaround (Message="@("hi")") tracked at dotnet/razor#13188
blazor/components/dynamiccomponent.md The IDictionary<string, object> boxing rule for union parameters (box as the union, not the case)
blazor/state-management/prerendered-state-persistence.md Unions work in PCS; user-configured JsonSerializerContext doesn't flow — use per-type [JsonUnion(TypeClassifier = ...)]

Each insert is one short paragraph plus a small example. Total added: ~173 lines. Net change to the repo: -162 lines about unions.

Deliberately not included

Per the "keep noise low" principle from the linked issues:

  • No insert in fundamentals/minimal-apis/parameter-binding.md — the responses.md insert already covers request-body behavior implicitly (same STJ rules either direction) and a second copy would be noise.
  • No insert in mvc/models/model-binding.md — same reasoning for the MVC side.
  • No new Blazor content for EventCallback<TUnion>, CascadingValue<TUnion>, generic TItem inference, trimming/AOT in WASM, or Hot Reload. These all "just work" — verified end-to-end against .NET 11 Preview 6 — and leaving them undocumented is intentional. They behave the same as any other generic type parameter.

Verification

Behaviors documented in the new inserts were verified against .NET 11 Preview 6 (11.0.100-preview.6.26315.102):

Open dependency

The new inserts link to /dotnet/standard/serialization/system-text-json/unions for serialization details. If that article doesn't yet exist in dotnet/docs, those links will redirect or 404 until it lands. Happy to update the links if there's a different canonical location.

Reviewers

Marking as draft — would love eyes from @guardrex on the Blazor inserts in particular, and from anyone on the team for the overall direction (in-context vs standalone) before this leaves draft.

Closes #37275
Addresses #37274


Internal previews

📄 File 🔗 Preview link
aspnetcore/blazor/components/dynamiccomponent.md aspnetcore/blazor/components/dynamiccomponent
aspnetcore/blazor/components/index.md aspnetcore/blazor/components/index
aspnetcore/blazor/state-management/prerendered-state-persistence.md aspnetcore/blazor/state-management/prerendered-state-persistence
aspnetcore/fundamentals/minimal-apis/responses.md aspnetcore/fundamentals/minimal-apis/responses
aspnetcore/fundamentals/openapi/aspnetcore-openapi.md aspnetcore/fundamentals/openapi/aspnetcore-openapi
aspnetcore/signalr/hubs.md aspnetcore/signalr/hubs
aspnetcore/toc.yml aspnetcore/toc
aspnetcore/web-api/action-return-types.md aspnetcore/web-api/action-return-types

Addresses #37275 (retire fundamentals/unions.md and integrate per-area)
and #37274 (Blazor-specific notes in the relevant Blazor articles).

C# unions are a language feature whose ASP.NET Core surface is small and
flows through existing serialization-driven features. A standalone article
under Fundamentals overstates the concept and forces readers to leave the
article they were already in.

This change:

* Deletes aspnetcore/fundamentals/unions.md and removes its TOC entry.
* Adds a redirect to fundamentals/minimal-apis/responses (the most common
  starting point for union usage).
* Adds one short, moniker-gated (>= aspnetcore-11.0) note in each of:
  - fundamentals/minimal-apis/responses.md
  - web-api/action-return-types.md
  - signalr/hubs.md (JsonHubProtocol only)
  - fundamentals/openapi/aspnetcore-openapi.md (anyOf schema)
  - blazor/components/index.md (union component parameters + Razor
    literal-attribute workaround, tracked at dotnet/razor#13188)
  - blazor/components/dynamiccomponent.md (boxing rule)
  - blazor/state-management/prerendered-state-persistence.md
    (JsonSerializerContext doesn't flow into the unions deserializer)

Each note links to the System.Text.Json unions article for the
serialization behavior (case selection, classifiers, ambiguity) that's
independent of ASP.NET Core.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@wadepickett

wadepickett commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

@danroth27, I know this is still in draft, but just a note to save you time: I can clean the moniker versioning /build issues for all articles in the PR, once you feel like the updates are otherwise how you want them. I'll fix in review.

What is happening:
For the moniker versioning issues, there are some topics that have:
:::moniker range=">= aspnetcore-10.0"

This is conflicting with the new sections that are dropped within that moniker block that are specifying: >= aspnetcore-11.0 since now there are two ranges competing for trying to handle any version equesl or greater than 11.

Usually we would create a new copy of the article that is for >= 11 going forward, which allows us to avoid any nested versioning speghetti and give a clear article dedicated to a version that can be archived or retired later.

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.

[Docs] Retire standalone C# unions article; integrate into existing articles in context

3 participants