Skip to content

Commit d024f41

Browse files
authored
Blazor RC1 coverage (#36053)
1 parent a2a2df6 commit d024f41

File tree

10 files changed

+280
-126
lines changed

10 files changed

+280
-126
lines changed

aspnetcore/blazor/forms/validation.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Learn how to use validation in Blazor forms.
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: wpickett
77
ms.custom: mvc
8-
ms.date: 11/12/2024
8+
ms.date: 09/08/2025
99
uid: blazor/forms/validation
1010
---
1111
# ASP.NET Core Blazor forms validation
@@ -134,6 +134,12 @@ The <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> compon
134134
* [`DataAnnotationsValidator`](https://github.com/dotnet/AspNetCore/blob/main/src/Components/Forms/src/DataAnnotationsValidator.cs)
135135
* [`EnableDataAnnotationsValidation`](https://github.com/dotnet/AspNetCore/blob/main/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs)
136136

137+
:::moniker range=">= aspnetcore-10.0"
138+
139+
For details on validation behavior, see the [`DataAnnotationsValidator` validation behavior](#dataannotationsvalidator-validation-behavior) section.
140+
141+
:::moniker-end
142+
137143
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>.
138144

139145
[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)]
@@ -1638,26 +1644,18 @@ In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Component
16381644

16391645
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.
16401646

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-
16501647
:::moniker-end
16511648

16521649
:::moniker range="< aspnetcore-10.0"
16531650

16541651
## Nested objects, collection types, and complex types
16551652

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.
16571655
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.
16591657

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:
16611659

16621660
```razor
16631661
<EditForm ...>
@@ -1703,6 +1701,8 @@ public class ShipDescription
17031701
}
17041702
```
17051703

1704+
:::moniker-end
1705+
17061706
## Enable the submit button based on form validation
17071707

17081708
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
18291829
}
18301830
}
18311831
```
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.
1844+
1845+
:::moniker-end

aspnetcore/blazor/fundamentals/routing.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Learn how to manage Blazor app request routing and how to use the N
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: wpickett
77
ms.custom: mvc
8-
ms.date: 11/12/2024
8+
ms.date: 09/08/2025
99
uid: blazor/fundamentals/routing
1010
---
1111
# ASP.NET Core Blazor routing and navigation
@@ -704,6 +704,31 @@ The following component:
704704

705705
For more information on component disposal, see <xref:blazor/components/component-disposal>.
706706

707+
:::moniker range=">= aspnetcore-9.0"
708+
709+
## Navigation Manager redirect behavior during static server-side rendering (static SSR)
710+
711+
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:
718+
719+
```xml
720+
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
721+
```
722+
723+
:::moniker-end
724+
725+
:::moniker range=">= aspnetcore-9.0 < aspnetcore-10.0"
726+
727+
> [!NOTE]
728+
> 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.
729+
730+
:::moniker-end
731+
707732
:::moniker range=">= aspnetcore-10.0"
708733

709734
## Not Found responses

aspnetcore/blazor/host-and-deploy/configure-trimmer.md

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Learn how to control the Intermediate Language (IL) Trimmer when bu
55
monikerRange: '>= aspnetcore-5.0'
66
ms.author: wpickett
77
ms.custom: mvc
8-
ms.date: 11/12/2024
8+
ms.date: 09/08/2025
99
uid: blazor/host-and-deploy/configure-trimmer
1010
---
1111
# Configure the Trimmer for ASP.NET Core Blazor
@@ -42,73 +42,123 @@ For more information, see [Trimming options (.NET documentation)](/dotnet/core/d
4242

4343
## Failure to preserve types used by a published app
4444

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.
4646

4747
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.
4848

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>>`).
50+
51+
`TrimExample.razor`:
5052

5153
```razor
52-
@rendermode @(new InteractiveWebAssemblyRenderMode(false))
54+
@page "/trim-example"
5355
@using System.Diagnostics.CodeAnalysis
5456
@using System.Text.Json
5557
56-
<dl>
58+
<h1>Trim Example</h1>
59+
60+
<ul>
5761
@foreach (var item in @items)
5862
{
59-
<dt>@item.Key</dt>
60-
<dd>@item.Value</dd>
63+
<li>@item.Item1, @item.Item2</li>
6164
}
62-
</dl>
65+
</ul>
6366
6467
@code {
65-
private List<KeyValuePair<string, string>> items = [];
68+
private List<Tuple<string, string>> items = [];
6669
6770
[StringSyntax(StringSyntaxAttribute.Json)]
6871
private const string data =
69-
"""[{"key":"key 1","value":"value 1"},{"key":"key 2","value":"value 2"}]""";
72+
"""[{"item1":"1:T1","item2":"1:T2"},{"item1":"2:T1","item2":"2:T2"}]""";
7073
7174
protected override void OnInitialized()
7275
{
7376
JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };
7477
7578
items = JsonSerializer
76-
.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
79+
.Deserialize<List<Tuple<string, string>>>(data, options)!;
7780
}
7881
}
7982
```
8083

81-
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:
90+
91+
> :::no-loc text="crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]":::
92+
> :::no-loc text="Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]":::
93+
> :::no-loc text="System.NotSupportedException: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]":::
94+
95+
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.
102+
103+
`StringTuple.cs`:
104+
105+
```csharp
106+
[method: SetsRequiredMembers]
107+
public sealed class StringTuple(string item1, string item2)
108+
{
109+
public required string Item1 { get; init; } = item1;
110+
public required string Item2 { get; init; } = item2;
111+
}
112+
```
113+
114+
The component is modified to use the `StringTuple` type:
115+
116+
```diff
117+
- private List<Tuple<string, string>> items = [];
118+
+ private List<StringTuple> items = [];
119+
```
120+
121+
```diff
122+
- items = JsonSerializer.Deserialize<List<Tuple<string, string>>>(data, options)!;
123+
+ items = JsonSerializer.Deserialize<List<StringTuple>>(data, options)!;
124+
```
125+
126+
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"
82129

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:
87131

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)
133+
* [Use a Root Descriptor](#use-a-root-descriptor)
89134

90-
> :::no-loc text="Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]":::
135+
:::moniker-end
91136

92-
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
93142

94143
### Preserve the type as a dynamic dependency
95144

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).
97146

98147
If not already present, add an `@using` directive for <xref:System.Diagnostics.CodeAnalysis?displayProperty=fullName>:
99148

100149
```razor
101150
@using System.Diagnostics.CodeAnalysis
102151
```
103152

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>:
105154

106155
```diff
107-
+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(KeyValuePair<string, string>))]
108-
private List<KeyValuePair<string, string>> items = [];
156+
+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors,
157+
+ typeof(Tuple<string, string>))]
158+
private List<Tuple<string, string>> items = [];
109159
```
110160

111-
<!-- UPDATE 10.0 - Hold this for https://github.com/dotnet/aspnetcore/issues/52947
161+
:::moniker range=">= aspnetcore-10.0"
112162

113163
### Use a Root Descriptor
114164

@@ -118,10 +168,8 @@ Add an `ILLink.Descriptors.xml` file to the root of the app&dagger; with the typ
118168

119169
```xml
120170
<linker>
121-
<assembly fullname="System.Runtime">
122-
<type fullname="System.Collections.Generic.KeyValuePair`2">
123-
<method signature="System.Void .ctor(TKey,TValue)" />
124-
</type>
171+
<assembly fullname="System.Private.CoreLib">
172+
<type fullname="System.Tuple`2" preserve="all" />
125173
</assembly>
126174
</linker>
127175
```
@@ -138,42 +186,21 @@ Add a `TrimmerRootDescriptor` item to the app's project file&Dagger; referencing
138186

139187
&Dagger;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).
140188

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.
152-
153-
`StringKeyValuePair.cs`:
189+
:::moniker-end
154190

155-
```csharp
156-
[method: SetsRequiredMembers]
157-
public sealed class StringKeyValuePair(string key, string value)
158-
{
159-
public required string Key { get; init; } = key;
160-
public required string Value { get; init; } = value;
161-
}
162-
```
191+
:::moniker range="= aspnetcore-8.0"
163192

164-
The component is modified to use the `StringKeyValuePair` type:
193+
### Workaround in .NET 8
165194

166-
```diff
167-
- private List<KeyValuePair<string, string>> items = [];
168-
+ private List<StringKeyValuePair> items = [];
169-
```
195+
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:
170196

171-
```diff
172-
- items = JsonSerializer.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
173-
+ items = JsonSerializer.Deserialize<List<StringKeyValuePair>>(data, options)!;
197+
```xml
198+
<PropertyGroup>
199+
<_ExtraTrimmerArgs>--keep-metadata parametername</_ExtraTrimmerArgs>
200+
</PropertyGroup>
174201
```
175202

176-
Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published.
203+
:::moniker-end
177204

178205
## Additional resources
179206

0 commit comments

Comments
 (0)