You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For details on validation behavior, see the [`DataAnnotationsValidator` validation behavior](#dataannotationsvalidator-validation-behavior) section.
140
+
141
+
:::moniker-end
142
+
137
143
If you need to enable data annotations validation support for an <xref:Microsoft.AspNetCore.Components.Forms.EditContext> in code, call <xref:Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation%2A> with an injected <xref:System.IServiceProvider> (`@inject IServiceProvider ServiceProvider`) on the <xref:Microsoft.AspNetCore.Components.Forms.EditContext>. For an advanced example, see the [`NotifyPropertyChangedValidationComponent` component in the ASP.NET Core Blazor framework's `BasicTestApp` (`dotnet/aspnetcore` GitHub repository)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor). In a production version of the example, replace the `new TestServiceProvider()` argument for the service provider with an injected <xref:System.IServiceProvider>.
@@ -1638,26 +1644,18 @@ In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Component
1638
1644
1639
1645
The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the new validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator.
1640
1646
1641
-
## Complex types
1642
-
1643
-
Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't complex-type properties.
1644
-
1645
-
To validate the bound model's entire object graph, including complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental*[`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package.
1646
-
1647
-
> [!NOTE]
1648
-
> The `ObjectGraphDataAnnotationsValidator` isn't compatible with [nested objects and collection types validation](#nested-objects-and-collection-types), but it's capable of validating nested objects and collection types on its own.
1649
-
1650
1647
:::moniker-end
1651
1648
1652
1649
:::moniker range="< aspnetcore-10.0"
1653
1650
1654
1651
## Nested objects, collection types, and complex types
1655
1652
1656
-
Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.
1653
+
> [!NOTE]
1654
+
> For apps targeting .NET 10 or later, we no longer recommend using the *experimental*[`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package and approach described in this section. We recommend using the built-in validation features of the the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component.
1657
1655
1658
-
To validate the bound model's entire object graph, including collection- and complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental*[`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package:
1656
+
Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> in .NET 9 or earlier only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.
1659
1657
1660
-
:::moniker-end
1658
+
To validate the bound model's entire object graph, including collection- and complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental*[`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package in .NET 9 or earlier:
1661
1659
1662
1660
```razor
1663
1661
<EditForm ...>
@@ -1703,6 +1701,8 @@ public class ShipDescription
1703
1701
}
1704
1702
```
1705
1703
1704
+
:::moniker-end
1705
+
1706
1706
## Enable the submit button based on form validation
1707
1707
1708
1708
To enable and disable the submit button based on form validation, the following example:
@@ -1829,3 +1829,17 @@ A side effect of the preceding approach is that a validation summary (<xref:Micr
1829
1829
}
1830
1830
}
1831
1831
```
1832
+
1833
+
:::moniker range=">= aspnetcore-10.0"
1834
+
1835
+
## `DataAnnotationsValidator` validation behavior
1836
+
1837
+
The <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component has the same validation order and short-circuiting behavior as <xref:System.ComponentModel.DataAnnotations.Validator?displayProperty=nameWithType>. The following rules are applied when validating an instance of type `T`:
1838
+
1839
+
1. Member properties of `T` are validated, including recursively validating nested objects.
1840
+
1. Type-level attributes of `T` are validated.
1841
+
1. The <xref:System.ComponentModel.DataAnnotations.IValidatableObject.Validate%2A?displayProperty=nameWithType> method is executed, if `T` implements it.
1842
+
1843
+
If one of the preceding steps produces a validation error, the remaining steps are skipped.
For a redirect during static server-side rendering (static SSR), <xref:Microsoft.AspNetCore.Components.NavigationManager> relies on throwing a <xref:Microsoft.AspNetCore.Components.NavigationException> that gets captured by the framework, which converts the error into a redirect. Code that exists after the call to <xref:Microsoft.AspNetCore.Components.NavigationManager.NavigateTo%2A> isn't called. When using Visual Studio, the debugger breaks on the exception, requiring you to deselect the checkbox for **Break when this exception type is user-handled** in the Visual Studio UI to avoid the debugger stopping for future redirects.
712
+
713
+
:::moniker-end
714
+
715
+
:::moniker range=">= aspnetcore-10.0"
716
+
717
+
You can use the `<BlazorDisableThrowNavigationException>` MSBuild property set to `true` in the app's project file to opt-in to no longer throwing a <xref:Microsoft.AspNetCore.Components.NavigationException>. This behavior is enabled by default in the .NET 10 or later Blazor Web App project template:
> In .NET 10 or later, you can opt-in to not throwing a <xref:Microsoft.AspNetCore.Components.NavigationException> by setting the `<BlazorDisableThrowNavigationException>` MSBuild property to `true` in the app's project file. To take advantage of the new MSBuild property and behavior, upgrade the app to .NET 10 or later.
Copy file name to clipboardExpand all lines: aspnetcore/blazor/host-and-deploy/configure-trimmer.md
+85-58Lines changed: 85 additions & 58 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@ description: Learn how to control the Intermediate Language (IL) Trimmer when bu
5
5
monikerRange: '>= aspnetcore-5.0'
6
6
ms.author: wpickett
7
7
ms.custom: mvc
8
-
ms.date: 11/12/2024
8
+
ms.date: 09/08/2025
9
9
uid: blazor/host-and-deploy/configure-trimmer
10
10
---
11
11
# Configure the Trimmer for ASP.NET Core Blazor
@@ -42,73 +42,123 @@ For more information, see [Trimming options (.NET documentation)](/dotnet/core/d
42
42
43
43
## Failure to preserve types used by a published app
44
44
45
-
Trimming may have detrimental effects for a published app leading to runtime errors. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations.
45
+
Trimming may have detrimental effects for a published app leading to runtime errors, even in spite of setting the [`<PublishTrimmed>` property](#configuration) to `false` in the project file. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations.
46
46
47
47
The IL Trimmer is also unable to react to an app's dynamic behavior at runtime. To ensure the trimmed app works correctly once deployed, test published output frequently while developing.
48
48
49
-
Consider the following client-side component in a Blazor Web App (.NET 8 or later) that deserializes a <xref:System.Collections.Generic.KeyValuePair> collection (`List<KeyValuePair<string, string>>`):
49
+
Consider the following example that performs JSON deserialization into a <xref:System.Tuple%602> collection (`List<Tuple<string, string>>`).
The preceding component executes normally when the app is run locally and produces the following rendered definition list (`<dl>`):
84
+
The preceding component executes normally when the app is run locally and produces the following rendered list:
85
+
86
+
> • 1:T1, 1:T2
87
+
> • 2:T2, 2:T2
88
+
89
+
When the app is published, <xref:System.Tuple%602> is trimmed from the app, even in spite of setting the `<PublishTrimmed>` property to `false` in the project file. Accessing the component throws the following exception:
To address lost types, consider adopting one of the following approaches.
96
+
97
+
### Custom types
98
+
99
+
Custom types aren't trimmed by Blazor when an app is published, so we recommend using custom types for JS interop, JSON serialization/deserialization, and other operations that rely on reflection.
100
+
101
+
The following modifications create a `StringTuple` type for use by the component.
Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published.
127
+
128
+
:::moniker range=">= aspnetcore-10.0"
82
129
83
-
> **:::no-loc text="key 1":::**
84
-
> :::no-loc text="value 1":::
85
-
> **:::no-loc text="key 2":::**
86
-
> :::no-loc text="value 2":::
130
+
If you prefer to use framework types in spite of our recommendation, use either of the following approaches:
87
131
88
-
When the app is published, <xref:System.Collections.Generic.KeyValuePair> is trimmed from the app, even in spite of setting the [`<PublishTrimmed>` property](#configuration) to `false` in the project file. Accessing the component throws the following exception:
132
+
*[Preserve the type as a dynamic dependency](#preserve-the-type-as-a-dynamic-dependency)
To address lost types, consider the following approaches.
137
+
:::moniker range="< aspnetcore-10.0"
138
+
139
+
If you prefer to use framework types in spite of our recommendation, [preserve the type as a dynamic dependency](#preserve-the-type-as-a-dynamic-dependency).
140
+
141
+
:::moniker-end
93
142
94
143
### Preserve the type as a dynamic dependency
95
144
96
-
We recommend creating a dynamic dependency to preserve the type with the [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute).
145
+
Create a dynamic dependency to preserve the type with the [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute).
97
146
98
147
If not already present, add an `@using` directive for <xref:System.Diagnostics.CodeAnalysis?displayProperty=fullName>:
99
148
100
149
```razor
101
150
@using System.Diagnostics.CodeAnalysis
102
151
```
103
152
104
-
Add a [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) to preserve the <xref:System.Collections.Generic.KeyValuePair>:
153
+
Add a [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) to preserve the <xref:System.Tuple%602>:
@@ -138,42 +186,21 @@ Add a `TrimmerRootDescriptor` item to the app's project file‡ referencing
138
186
139
187
‡The project file is either the project file of the Blazor WebAssembly app or the project file of the `.Client` project of a Blazor Web App (.NET 8 or later).
140
188
141
-
-->
142
-
143
-
### Custom types
144
-
145
-
<!-- UPDATE 10.0 - We'll hold this for when the file descriptor approach comes back.
146
-
147
-
Custom types aren't trimmed by Blazor when an app is published, but we recommend [preserving types as dynamic dependencies](#preserve-the-type-as-a-dynamic-dependency) instead of creating custom types.
148
-
149
-
-->
150
-
151
-
The following modifications create a `StringKeyValuePair` type for use by the component.
As a workaround in .NET 8, you can add the `_ExtraTrimmerArgs` MSBuild property set to `--keep-metadata parametername` in the app's project file to preserve parameter names during trimming:
0 commit comments