Summary
Once #67094 ships an official mcr.microsoft.com/dotnet/blazor-gateway image, we should coordinate with the Aspire team to remove the inlined Gateway.cs.in source template from Aspire.Hosting.Blazor and replace it with the precompiled gateway binary.
This tracks the ASP.NET Core side of the work (publishing the gateway in a shape Aspire can consume); the Aspire side will need its own follow-up issue in dotnet/aspire.
Motivation
Aspire currently ships the Blazor gateway as a source file (Gateway.cs.in) inside Aspire.Hosting.Blazor. At AppHost startup, Aspire writes the file to disk, invokes dotnet run against it, and orchestrates the resulting process. This:
- Recompiles the gateway every time an Aspire AppHost runs.
- Forks the gateway implementation between ASP.NET Core (
Microsoft.AspNetCore.Components.Gateway) and Aspire (Gateway.cs.in), so fixes have to be applied in both places.
- Cannot trivially pick up gateway updates without a corresponding
Aspire.Hosting.Blazor release.
Once ASP.NET Core ships the gateway as a redistributable artifact (which it already does — the package's tools/blazor-gateway.dll layout is consumable by anyone willing to extract it), Aspire can bundle the precompiled binary and exec it directly.
Proposal
Aspire.Hosting.Blazor adopts the same pattern they already use for shipping the Aspire Dashboard binaries — bundle the gateway tools/ payload inside Aspire.Hosting.Blazor.nupkg itself, then resolve the path at runtime from the integration assembly's location.
Sketch on the Aspire side (for context, not part of this work)
<!-- Aspire.Hosting.Blazor.csproj -->
<PackageReference Include="Microsoft.AspNetCore.Components.Gateway"
Version="$(MicrosoftAspNetCoreComponentsGatewayVersion)"
PrivateAssets="all"
GeneratePathProperty="true"
ExcludeAssets="build;buildTransitive;runtime;compile;native" />
<Target Name="_BundleBlazorGateway" BeforeTargets="GenerateNuspec">
<ItemGroup>
<None Include="$(PkgMicrosoft_AspNetCore_Components_Gateway)\tools\**\*"
Pack="true"
PackagePath="tools\blazor-gateway\" />
</ItemGroup>
</Target>
At runtime:
var gatewayDll = Path.Combine(
Path.GetDirectoryName(typeof(BlazorGatewayResource).Assembly.Location)!,
"..", "..", "tools", "blazor-gateway", "blazor-gateway.dll");
(Aspire already does the equivalent for the dashboard — exact path layout TBD on their side.)
Why Shape B (bundle inside the Aspire package)
Discussed in #67094 and on the original prototype thread. The alternatives:
- Transitive
<PackageReference> (let the gateway flow from Aspire.Hosting.Blazor to the AppHost): works, but exposes the gateway as a transitive dependency in the user's AppHost (dotnet list package --include-transitive). Cosmetic leak.
dotnet tool exec (requires PackAsTool=true): rejected, see #67092 — NU1212 makes PackAsTool mutually exclusive with <PackageReference> consumption, which would break the standalone WASM template.
- Container image (#67094): the right answer for production / K8s / ACA scenarios but forces Docker on dev machines for local F5.
- Bundle inside
Aspire.Hosting.Blazor: user adds Aspire.Hosting.Blazor and gets everything — no extra package, no transitive surprises, no Docker requirement for local F5. Aspire owns the snapshot version. Already the pattern Aspire uses for other bundled binaries.
ASP.NET Core side work (this issue)
The gateway's existing package shape is already compatible with Shape B — no source changes are required in dotnet/aspnetcore. Specifically:
tools/blazor-gateway.dll flat layout is preserved.
- All transitive runtime dependencies (OpenTelemetry, YARP, Service Discovery, Polly, HealthChecks) are already under
tools/.
tools/blazor-gateway.runtimeconfig.json points at Microsoft.AspNetCore.App 11.0.0 with rollForwardOnNoCandidateFx: 2.
The only ASP.NET Core side commitments we're making by accepting this proposal:
Sequencing
- #67048 merges — service-defaults template + current gateway improvements.
- #67094 lands in
dotnet/dotnet-docker — mcr.microsoft.com/dotnet/blazor-gateway image published.
- This issue — file an
dotnet/aspire companion issue requesting Aspire bundle the gateway via Shape B and delete Gateway.cs.in. Document the tools/ contract on our side.
References
Summary
Once #67094 ships an official
mcr.microsoft.com/dotnet/blazor-gatewayimage, we should coordinate with the Aspire team to remove the inlinedGateway.cs.insource template fromAspire.Hosting.Blazorand replace it with the precompiled gateway binary.This tracks the ASP.NET Core side of the work (publishing the gateway in a shape Aspire can consume); the Aspire side will need its own follow-up issue in
dotnet/aspire.Motivation
Aspire currently ships the Blazor gateway as a source file (
Gateway.cs.in) insideAspire.Hosting.Blazor. At AppHost startup, Aspire writes the file to disk, invokesdotnet runagainst it, and orchestrates the resulting process. This:Microsoft.AspNetCore.Components.Gateway) and Aspire (Gateway.cs.in), so fixes have to be applied in both places.Aspire.Hosting.Blazorrelease.Once ASP.NET Core ships the gateway as a redistributable artifact (which it already does — the package's
tools/blazor-gateway.dlllayout is consumable by anyone willing to extract it), Aspire can bundle the precompiled binary and exec it directly.Proposal
Aspire.Hosting.Blazoradopts the same pattern they already use for shipping the Aspire Dashboard binaries — bundle the gatewaytools/payload insideAspire.Hosting.Blazor.nupkgitself, then resolve the path at runtime from the integration assembly's location.Sketch on the Aspire side (for context, not part of this work)
At runtime:
(Aspire already does the equivalent for the dashboard — exact path layout TBD on their side.)
Why Shape B (bundle inside the Aspire package)
Discussed in #67094 and on the original prototype thread. The alternatives:
<PackageReference>(let the gateway flow fromAspire.Hosting.Blazorto the AppHost): works, but exposes the gateway as a transitive dependency in the user's AppHost (dotnet list package --include-transitive). Cosmetic leak.dotnet tool exec(requiresPackAsTool=true): rejected, see #67092 — NU1212 makesPackAsToolmutually exclusive with<PackageReference>consumption, which would break the standalone WASM template.Aspire.Hosting.Blazor: user addsAspire.Hosting.Blazorand gets everything — no extra package, no transitive surprises, no Docker requirement for local F5. Aspire owns the snapshot version. Already the pattern Aspire uses for other bundled binaries.ASP.NET Core side work (this issue)
The gateway's existing package shape is already compatible with Shape B — no source changes are required in
dotnet/aspnetcore. Specifically:tools/blazor-gateway.dllflat layout is preserved.tools/.tools/blazor-gateway.runtimeconfig.jsonpoints atMicrosoft.AspNetCore.App 11.0.0withrollForwardOnNoCandidateFx: 2.The only ASP.NET Core side commitments we're making by accepting this proposal:
tools/layout as a public contract for at least one supported consumer (Aspire). Document the shape (folder structure, dll names, runtimeconfig framework references) so it can't be silently broken.compile/runtime/buildassets (it already does, since there's nolib/and the onlybuild/content is the WASM-template.targetsfile which Aspire excludes).MicrosoftAspNetCoreComponentsGatewayVersioninAspire.Hosting.Blazorcan be advanced predictably (every preview / GA).Sequencing
dotnet/dotnet-docker—mcr.microsoft.com/dotnet/blazor-gatewayimage published.dotnet/aspirecompanion issue requesting Aspire bundle the gateway via Shape B and deleteGateway.cs.in. Document thetools/contract on our side.References
PackAsTool(closed as won't-fix, NU1212 blocker)Aspire.Hosting.Blazor/Scripts/Gateway.cs.in— the inlined source template to be replacedAspire/playground/BlazorStandalone/BlazorStandalone.ClientServiceDefaults— reference Aspire integration