dotnet / dotnet/aspnetcore

Blazor Server Native AOT publish emits detailed trimming and AOT warnings on .NET 11

Open
#69,270 1 comment 0 reactions 0 assignees View on GitHub
area-blazor NativeAOT
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

### Is there an existing issue for this?

I searched existing issues. This is closely related to https://github.com/dotnet/aspnetcore/issues/51598 (the Blazor Native AOT umbrella) and https://github.com/dotnet/aspnetcore/issues/42676 (component assembly trim warnings). This issue provides a current, concrete .NET 11 RC warning inventory from a real Blazor Server application and could be tracked as a child or concrete work item under those issues.

https://github.com/dotnet/aspnetcore/issues/69217 tracks two additional dashboard Native AOT `IL2026` errors in `Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlAttributePropertyHelper.GetValue` and `GetProperties`. A metadata-update handler roots those trim-unsafe methods in Debug builds even though the application does not use MVC view rendering. Those exact diagnostics are not part of the Release warning inventory below: the Release compiler disables `System.Reflection.Metadata.MetadataUpdater`, so that retention path is absent. The issue is nevertheless directly related to the same dashboard AOT warning cleanup and should be considered alongside this inventory when validating Debug and Release configurations.

### Describe the bug

Aspire is enabling Native AOT for its server-side Blazor dashboard. Publishing with detailed trimmer diagnostics enabled succeeds only when a broad set of ASP.NET Core and JS interop diagnostic IDs are kept as warnings rather than errors.

The publish produces 70 distinct diagnostics directly in ASP.NET Core/JS interop code, plus diagnostics propagated to generated Razor and application call sites.

### Warning breakdown

#### `IL2026`: APIs marked incompatible with trimming (20 direct diagnostics, plus 2 application call-site diagnostics)

The first two warnings occur immediately at the application boundary:

1. `AddRazorComponents(...)` states that Razor Components does not currently support trimming or Native AOT.
2. `AddInteractiveServerComponents(...)` states that server-side Blazor does not currently support Native AOT.

The remaining framework implementation warnings fall into these paths:

3. Component discovery calls `Assembly.GetExportedTypes()` in `IRazorComponentApplication.CreatePageRouteCollection`.
4. Form mapping constructs `FormDataMapperOptions` from both `RazorComponentsServiceOptions` and `DefaultRazorComponentsServiceOptionsConfiguration`.
5. Endpoint marker rendering serializes `ComponentMarker` and `ComponentEndMarker` in `EndpointHtmlRenderer`.
6. Stored component state uses reflection-based JSON serialization in `JsonStoredDataSerializer.DeserializeEntry`, `DeserializeValue`, and `WriteEntry`.
7. Temp-data providers deserialize dictionaries in `CookieTempDataProvider` and `SessionStorageTempDataProvider`.
8. Resource collection and import-map generation serialize `IReadOnlyList` and `ImportMapDefinition`.
9. WebAssembly component invocation serializes parameter/value lists in `WebAssemblyComponentSerializer`.
10. Circuit persistence deserializes the root-component marker dictionary.
11. Server component parameter deserialization uses both runtime `Type` and `SerializedRenderFragment` JSON overloads.
12. Circuit JavaScript initialization writes a JSON list without generated type metadata.
13. Default Razor component options deserialize a JSON string array without generated metadata.

Most JSON-related warnings can potentially use `JsonTypeInfo` or a framework source-generated context. Component discovery, form mapping, and runtime-typed component parameter/state serialization need separate designs or need to remain explicitly unsupported at a narrow boundary.

#### `IL2057`: persisted-state type names cannot be statically resolved (1 diagnostic)

`JsonStoredDataSerializer.ResolveType(string)` passes a runtime string to `Type.GetType`. The trimmer cannot determine which type will be requested or preserve it.

An AOT-safe design needs a generated or application-provided mapping from stable type identifiers to preserved `Type` values. Merely suppressing the warning can otherwise lead to a missing type at runtime.

#### `IL2062`: discovered routable component type loses annotations (1 diagnostic)

`IRazorComponentApplication.CreatePageRouteCollection` discovers exported types at runtime and assigns one to `PageComponentBuilder.PageType`. The discovered value cannot be proven to satisfy the destination's `DynamicallyAccessedMembers` requirements.

Generated route/component metadata could supply statically known and correctly annotated page types without scanning `Assembly.GetExportedTypes()`.

#### `IL2065`: JS-invokable method discovery scans an unknown type (1 diagnostic)

`DotNetDispatcher.ScanAssemblyForCallableMethods` calls `Type.GetMethods` on a type discovered while scanning an assembly. The type value has no preservation annotation, so `[JSInvokable]` methods may be trimmed.

This likely needs generated JS-invokable method metadata, explicit roots, or annotation flow from the assembly/type discovery boundary.

#### `IL2067`: remote renderer drops component-type requirements (1 diagnostic)

`RemoteRenderer.AddComponentAsync(Type, ...)` passes `componentType` to `WebRenderer.AddRootComponent`, which requires all component members. The source parameter does not carry the same `DynamicallyAccessedMembers(All)` requirement.

Propagating the annotation through `RemoteRenderer` and its callers would make the contract explicit. A generated root-component registry could avoid the runtime `Type` flow entirely.

#### `IL2072`: component/property type values lack required preserved members (7 diagnostics)

1. `ComponentFactory.PerformPropertyInjection` obtains a component type via `object.GetType()` and passes it to `IComponentPropertyActivator.GetActivator`, which requires all members.
2. `CascadingParameterState.FindCascadingParameters` similarly passes `object.GetType()` to a cache requiring all members.
3. `ComponentProperties.SetProperties` uses `object.GetType()` for both `ThrowForUnknownIncomingParameterName` and `WritersForType`; both destinations require all members.
4. `SessionCascadingValueSupplier.CreateSubscription` forwards `CascadingParameterInfo.PropertyType` to a subscription that requires public constructors, fields, and properties.
5. `CircuitHost.PerformRootComponentOperations` passes `WebRootComponentDescriptor.ComponentType` to both add and update operations, which require all members.

These warnings require preservation requirements to flow through component instances, descriptors, cascading-parameter metadata, and circuit operations. Generated component activation/property-writing metadata could reduce the broad `All` requirements.

#### `IL2080`: cached cascading-value keys erase property annotations (2 diagnostics)

`SessionCascadingValueSupplier.PropertyGetterFactory` and `TempDataCascadingValueSupplier.PropertyGetterFactory` cache a `(Type, string)` tuple, then call `Type.GetProperty` with public and non-public binding flags. `ValueTuple.Item1` cannot carry the necessary member-preservation annotation.

A dedicated annotated key type, precomputed getter, or generated accessor would preserve the requirement across the cache boundary.

#### `IL2101`: invalid cross-assembly linker substitution XML (1 defect reported twice)

`Microsoft.AspNetCore.Components.Endpoints` embeds `ILLink.Substitutions.xml` whose `` names `Microsoft.AspNetCore.Components.Forms`. Linker XML embedded in one assembly cannot target another assembly this way, so the linker reports the same defect during analysis and final Native AOT compile.

The substitution should move to the Forms assembly or use the supported cross-assembly descriptor mechanism.

```text
resource ILLink.Substitutions.xml in Microsoft.AspNetCore.Components.Endpoints(2,4): warning IL2101: embedded XML contains an assembly fullname for Microsoft.AspNetCore.Components.Forms.
```

#### `IL2110`: Razor-generated code reflects over annotated fields (5 propagated diagnostics)

1. `Router.NotFoundPage`'s backing field is reflected over when a dashboard `Router` is opened by generated render code.
2. `LayoutView.Layout` and its captured `layoutType` field are reflected over when a dashboard `LayoutView` is opened.
3. `DynamicComponent.Type`'s backing field is reflected over when generated render code opens `DynamicComponent`.

These warnings occur in generated/application render methods, but the annotated members and reflection behavior belong to framework components. Generated Razor code could preserve only the required component parameters instead of causing broad member reflection over the framework component type.

#### `IL2111`: Razor/JS interop reflects over annotated methods (8 propagated and 1 direct diagnostic)

1. Generated `Router` code reflects over `Router.NotFoundPage.set`.
2. Generated `LayoutView` code reflects over `LayoutView.Layout.set` and `LayoutView.WrapInLayout`.
3. Generated `DynamicComponent` code reflects over `DynamicComponent.Type.set`.
4. `DotNetDispatcher.GetCachedMethodInfo` reflects over its own annotated `ScanTypeForCallableMethods(Type)` helper.

As with `IL2110`, the component warnings are reported at consumer call sites but originate from framework component reflection. The JS interop warning is direct framework code. Typed/generated parameter setting and JS-invokable registries would avoid reflecting over annotated members.

#### `IL3050`: runtime generic construction and reflection-based JSON (34 direct diagnostics, plus propagated `Virtualize` diagnostics)

The 34 direct framework warnings break down as follows:

1. **Form mapping:** constructing `FormDataMapperOptions` from service options may require runtime code generation.
2. **Runtime parsing:** `BindConverter.ParserDelegateCache` calls `MethodInfo.MakeGenericMethod`.
3. **Runtime component/state types:** persistent-value serialization calls `Type.MakeGenericType`, while persisted component state serializes and deserializes using runtime `Type` values.
4. **Endpoint JSON:** cookie/session temp data, resource collections, import maps, endpoint component markers, stored data, and WebAssembly invocation metadata use reflection-based `JsonSerializer` overloads.
5. **Server circuit JSON:** circuit root markers and component parameters use reflection-based `JsonSerializer` overloads.
6. **Rendering and JS components:** `WebRenderer`, `JSComponentInterop`, and render-fragment serialization use reflection-based JSON metadata.
7. **JS interop:** `JSRuntime.InvokeAsync`, `DotNetDispatcher.BeginInvokeDotNet`, `EndInvokeDotNetAfterTask`, and `ParseArguments` use reflection-based JSON; `DotNetDispatcher.GetTaskByType` calls `MakeGenericMethod`; and `TaskGenericsUtil.GetTaskResult` calls `MakeGenericType`.
8. **Circuit initialization:** `CircuitJavaScriptInitializationMiddleware` writes JSON without a `JsonTypeInfo`.

There is also a separate propagated warning family: opening `Virtualize` roots its nested `InitialIndexPhase` enum together with inherited `Enum.GetValues(Type)`. In this repro that appears in the generated `TextVisualizer.CreateVirtualize_*` method and `LogViewer.BuildRenderTree`.

Source-generated framework JSON contexts can address the closed framework data shapes. Runtime component parameter/state values need an extensibility mechanism for application-provided `JsonTypeInfo`. Generic task/parser construction and the `Virtualize` enum path need AOT-safe generic implementations.

```text
ILC : AOT analysis warning IL3050: Microsoft.JSInterop.Infrastructure.DotNetDispatcher.ParseArguments(...): JsonSerializer.Deserialize(Utf8JsonReader&, Type, JsonSerializerOptions) can break functionality when AOT compiling.
```

### Expected Behavior

A server-side Blazor application that uses supported features should publish with Native AOT without framework implementation warnings. Unsupported features should be isolated or produce actionable diagnostics at their public API boundaries rather than exposing many internal warning paths.

### Steps To Reproduce

1. Check out https://github.com/microsoft/aspire/pull/19565 after its warning-visibility change is included.
2. From the Aspire repository root, run:

```powershell
.\restore.cmd
.\.dotnet\dotnet.exe publish src\Aspire.Dashboard\Aspire.Dashboard.csproj `
-c Release -r win-x64 --self-contained true `
-p:ContinuousIntegrationBuild=false
```

3. Search the output for `Microsoft.AspNetCore`, `Microsoft.JSInterop`, `RazorSourceGenerator`, and `ILLink.Substitutions.xml`.

The project sets:

```xml
true
false
```

The application roots its own dashboard assembly and supplies source-generated JSON contexts for its application serialization. The remaining warnings listed here originate in framework code or are propagated by framework component APIs.

### .NET Version

`11.0.100-rc.1.26425.128`

### Anything else?

- ASP.NET Core: `Microsoft.AspNetCore.App 11.0.0-rc.1.26425.128`
- OS: Windows 10.0.26200, `win-x64`
- Repro application: https://github.com/microsoft/aspire/pull/19565
- Aspire commit: `ca26d2c4b5285508e07ee1b218ba0b36582d0e3e`
- Related: https://github.com/dotnet/aspnetcore/issues/51598, https://github.com/dotnet/aspnetcore/issues/42676, and https://github.com/dotnet/aspnetcore/issues/69217.

Contributor guide

Open the contributing guide

Research direction

Start with the warning breakdown and trace one diagnostic to the named ASP.NET Core or JS interop entry point. For the IL2101 warning, inspect the embedded ILLink.Substitutions.xml in Microsoft.AspNetCore.Components.Endpoints and the reported target assembly, Microsoft.AspNetCore.Components.Forms. The issue describes a broad inventory rather than a bounded fix; the stated goal is warning-free Native AOT publishing for supported features, with unsupported features isolated or diagnosed at public API boundaries.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.