HtmlAttributePropertyHelper metadata-update handler roots trim-unsafe methods in Native AOT builds
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
### Describe the bug
`Microsoft.AspNetCore.Mvc.ViewFeatures` registers `HtmlAttributePropertyHelper` itself as a metadata-update handler:
```csharp
[assembly: MetadataUpdateHandler(typeof(HtmlAttributePropertyHelper))]
```
`MetadataUpdateHandlerAttribute` requires preservation of **all public and non-public methods** on its handler type. Consequently, retaining this assembly attribute can preserve `HtmlAttributePropertyHelper.GetProperties` and `GetValue`, not just the intended `ClearCache` callback. Those methods call trim-unsafe `PropertyHelper` APIs and produce IL2026 during Native AOT compilation.
We encountered this while publishing the Aspire Dashboard in Debug with trimming warnings treated as errors. The Dashboard references this assembly through `SkipStatusCodePagesAttribute`; it does not directly call `HtmlAttributePropertyHelper` or render MVC views. Its Native AOT dependency graph confirms that the helper methods are retained for reflection, rather than through ordinary application calls.
This report is about unnecessary retention through the metadata-update registration, not a request to make MVC view rendering fully Native AOT compatible.
### Expected behavior
Retaining MVC assembly metadata and the metadata-update callback should not also preserve unrelated property-access methods and introduce IL2026 warnings in a consumer that does not use those methods.
### Reproduction and observed failure
Existing failing CI job (Windows ARM64, Debug):
https://github.com/microsoft/aspire/actions/runs/34559657169/job/103139652826
The same two errors were reproduced locally on Windows x64 using the Aspire repository at commit [`16156c13e19d93856736d352c1a0c73689296982`](https://github.com/microsoft/aspire/tree/16156c13e19d93856736d352c1a0c73689296982), before the dependency-warning workaround was added. With the repository's Windows/native build prerequisites installed, restore its pinned SDK and publish from the repository root:
```powershell
.\restore.cmd
.\.dotnet\dotnet.exe publish src/Aspire.Dashboard/Aspire.Dashboard.csproj `
-c Debug -r win-x64 -p:PlatformName=win-x64 `
-p:IlcTreatWarningsAsErrors=true -p:TreatWarningsAsErrors=true
```
Diagnostics (local paths omitted):
```text
ILC : Trim analysis error IL2026: Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlAttributePropertyHelper.GetValue(Object): Using member 'Microsoft.Extensions.Internal.PropertyHelper.GetValue(Object)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. This API is not trim safe.
ILC : Trim analysis error IL2026: Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlAttributePropertyHelper.GetProperties(Type): Using member 'Microsoft.Extensions.Internal.PropertyHelper.GetProperties(Type,ConcurrentDictionary`2)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. This API is not trim safe.
```
This is an existing application reproduction, not an independently validated minimal repro. Merely constructing `SkipStatusCodePagesAttribute` in a small app has not been established as sufficient; retention also involves assembly custom-attribute processing.
### Retention analysis
The Dashboard directly constructs `SkipStatusCodePagesAttribute` in its [routing extension](https://github.com/microsoft/aspire/blob/16156c13e19d93856736d352c1a0c73689296982/src/Aspire.Dashboard/Utils/RoutingExtensions.cs#L14). Its OTLP services also use the attribute.
The Debug ILC dependency graph shows:
```text
Dashboard RoutingExtensions.SkipStatusCodePages callback
-> constructed SkipStatusCodePagesAttribute
-> attribute type metadata
-> reflectable Microsoft.AspNetCore.Mvc.ViewFeatures module
Reflectable MVC module + ScopeDefinition.get_CustomAttributes
-> reflectable HtmlAttributePropertyHelper.GetProperties/GetValue
-> compiled methods
-> dataflow analysis producing IL2026
```
The corresponding framework source explains the broad preservation:
- [HtmlAttributePropertyHelper assembly registration and helper methods](https://github.com/dotnet/dotnet/blob/3551975be08744f0418857c5bed8ab1545c5dd47/src/aspnetcore/src/Mvc/Mvc.ViewFeatures/src/HtmlAttributePropertyHelper.cs).
- [MetadataUpdateHandlerAttribute](https://github.com/dotnet/dotnet/blob/3551975be08744f0418857c5bed8ab1545c5dd47/src/runtime/src/libraries/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdateHandlerAttribute.cs) annotates its handler type with `DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods`.
The registration is also still present on ASP.NET Core `main` when checked on 2026-09-11. The graph establishes compile-time retention, not that Hot Reload or these helper methods actually execute in the native application.
### Possible fix
Consider moving the metadata-update callback to a dedicated handler type containing only the cache-clearing callback, instead of registering the entire property-access helper type. This should avoid preserving unrelated methods through the handler's broad preservation contract. This proposed change has not yet been implemented or validated.
A focused trimming/Native AOT regression test should exercise assembly custom-attribute retention without invoking the property-access helpers, and verify that the handler registration does not root those methods or emit these IL2026 warnings. Cache invalidation during Hot Reload should remain covered as well.
### Environment and additional context
- Target framework: `net11.0`.
- .NET SDK: `11.0.100-rc.1.26425.128`.
- Runtime/ILCompiler: `11.0.0-rc.1.26425.128`; VMR source commit `3551975be08744f0418857c5bed8ab1545c5dd47`.
- Observed in Windows ARM64 Debug CI and reproduced locally with Windows x64 Debug Native AOT publishing.
- The Release publish did not emit these warnings. Its compiler response explicitly disables `System.Reflection.Metadata.MetadataUpdater.IsSupported`, while Debug's does not. This is an observed configuration difference, not an isolated proof of the Debug/Release cause.
- Aspire currently works around this by grouping/suppressing warnings specifically for `Microsoft.AspNetCore.Mvc.ViewFeatures`, preserving detailed analysis for first-party code. Strict Debug native publishes then succeed for x64 and ARM64; the ARM64 binary was not executed locally.
- Earlier PR #58558 fixed this handler's callback signature. This report concerns the separate trimming consequence of registering the full helper type.
Contributor guide
Research direction
Start with HtmlAttributePropertyHelper.cs and the MetadataUpdateHandlerAttribute source to trace which members the assembly registration preserves. Reproduce the warning with the documented Aspire Dashboard Native AOT publish command, then add a focused trimming regression test covering custom-attribute retention and Hot Reload cache invalidation. Done means the helper methods are not rooted through registration and the IL2026 warnings no longer occur.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100