diff --git a/aspnetcore/blazor/forms/binding.md b/aspnetcore/blazor/forms/binding.md index 3c0bab64f5c2..4c5c30d64ada 100644 --- a/aspnetcore/blazor/forms/binding.md +++ b/aspnetcore/blazor/forms/binding.md @@ -5,7 +5,7 @@ author: guardrex description: Learn how to use binding in Blazor forms. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett -ms.date: 09/22/2026 +ms.date: 09/23/2026 uid: blazor/forms/binding --- # ASP.NET Core Blazor forms binding @@ -58,6 +58,8 @@ Assignment to parameter of `EditForm`, the form creates a new `EditContext`. The new context starts with no modified fields or validation messages. + ## Context binding Assignment to : diff --git a/aspnetcore/blazor/forms/validation.md b/aspnetcore/blazor/forms/validation.md index 0b41751841fe..3eeb77ef6b0a 100644 --- a/aspnetcore/blazor/forms/validation.md +++ b/aspnetcore/blazor/forms/validation.md @@ -5,7 +5,7 @@ author: guardrex description: Learn how to use validation in Blazor forms. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett -ms.date: 09/22/2026 +ms.date: 09/23/2026 uid: blazor/forms/validation --- # ASP.NET Core Blazor forms validation @@ -246,7 +246,7 @@ Custom attributes can resolve registered services through . Don't assign both `Model` and `EditContext` to the same form. +`EditForm` creates an `EditContext` automatically when its `Model` parameter is assigned. To use validation APIs directly, create the context yourself and assign it to the parameter of `EditForm`. Don't assign both `Model` and `EditContext` to the same form. Custom validation commonly uses: @@ -360,6 +360,8 @@ Use to displa ``` +The `For` expression identifies the field by its model instance and property. Blazor represents this identity with a , so properties with the same name on different model instances are treated as different fields. Using an expression instead of a string also allows refactoring tools to update the property reference. + Use to display messages for the form: ```razor @@ -474,6 +476,20 @@ editContext.SetFieldCssClassProvider( For custom input markup, call to obtain the class selected by the current provider. +When an `EditForm` is assigned a model, its child content receives the generated `EditContext`. Capture the context through the `Context` parameter and call `FieldCssClass` to apply the field's classes to surrounding markup: + +```razor + + + +
+ + +
+
+``` + :::moniker-end diff --git a/aspnetcore/fundamentals/validation.md b/aspnetcore/fundamentals/validation.md index fc6082ed5fcd..24bec0b67a0c 100644 --- a/aspnetcore/fundamentals/validation.md +++ b/aspnetcore/fundamentals/validation.md @@ -5,7 +5,7 @@ author: Youssef1313 description: Use Microsoft.Extensions.Validation in ASP.NET Core to validate models. monikerRange: '>= aspnetcore-10.0' ms.author: ygerges -ms.date: 09/22/2026 +ms.date: 09/23/2026 uid: fundamentals/validation --- # Validation in ASP.NET Core @@ -270,6 +270,55 @@ Blazor form validation calls the asynchronous path for per-field validation and If your implementation can't support the synchronous path, throw . +The following example performs property-level asynchronous validation with a custom `AsyncValidationAttribute`. The attribute uses a hypothetical `IUserService` to check whether an email address is already registered. Empty values are left to , and the cancellation token is passed to the asynchronous operation: + +```csharp +using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.DependencyInjection; + +public sealed class UniqueEmailAttribute : AsyncValidationAttribute +{ + public UniqueEmailAttribute() + { + ErrorMessage = "Email is already registered."; + } + + protected override async Task IsValidAsync( + object? value, + ValidationContext validationContext, + CancellationToken cancellationToken) + { + if (value is not string email || string.IsNullOrWhiteSpace(email)) + { + return ValidationResult.Success; + } + + var userService = + validationContext.GetRequiredService(); + + return await userService.IsEmailExistsAsync( + email, cancellationToken) + ? new ValidationResult( + FormatErrorMessage(validationContext.DisplayName), + [validationContext.MemberName!]) + : ValidationResult.Success; + } + + protected override ValidationResult? IsValid( + object? value, ValidationContext validationContext) + { + throw new InvalidOperationException( + "Synchronous validation isn't supported."); + } +} + +public class RegistrationModel +{ + [Required, EmailAddress, UniqueEmail] + public string Email { get; set; } = string.Empty; +} +``` + The following example shows object-level asynchronous validation with `IAsyncValidatableObject`. It uses a hypothetical `IUserService` to check a database for an existing email address. Because the rule requires asynchronous I/O, the required synchronous `Validate` implementation throws . ```csharp