Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion aspnetcore/blazor/forms/binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -58,6 +58,8 @@ Assignment to <xref:Microsoft.AspNetCore.Components.Forms.EditForm.Model?display

:::moniker-end

When a different object instance is assigned to the <xref:Microsoft.AspNetCore.Components.Forms.EditForm.Model> parameter of `EditForm`, the form creates a new `EditContext`. The new context starts with no modified fields or validation messages.

## Context binding

Assignment to <xref:Microsoft.AspNetCore.Components.Forms.EditForm.EditContext?displayProperty=nameWithType>:
Expand Down
20 changes: 18 additions & 2 deletions aspnetcore/blazor/forms/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -246,7 +246,7 @@ Custom attributes can resolve registered services through <xref:System.Component

## Add validation through `EditContext`

`EditForm` creates an `EditContext` automatically when its `Model` parameter is assigned. To use validation APIs directly, create the `EditContext` yourself and assign it to <xref:Microsoft.AspNetCore.Components.Forms.EditForm.EditContext%2A>. 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 <xref:Microsoft.AspNetCore.Components.Forms.EditForm.EditContext> parameter of `EditForm`. Don't assign both `Model` and `EditContext` to the same form.
Comment thread
oroztocil marked this conversation as resolved.

Custom validation commonly uses:

Expand Down Expand Up @@ -360,6 +360,8 @@ Use <xref:Microsoft.AspNetCore.Components.Forms.ValidationMessage%601> to displa
<ValidationMessage For="() => Model.Identifier" />
```

The `For` expression identifies the field by its model instance and property. Blazor represents this identity with a <xref:Microsoft.AspNetCore.Components.Forms.FieldIdentifier>, 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 <xref:Microsoft.AspNetCore.Components.Forms.ValidationSummary> to display messages for the form:

```razor
Expand Down Expand Up @@ -474,6 +476,20 @@ editContext.SetFieldCssClassProvider(

For custom input markup, call <xref:Microsoft.AspNetCore.Components.Forms.EditContextFieldClassExtensions.FieldCssClass%2A> 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
<EditForm Model="Model" Context="editContext">
<DataAnnotationsValidator />

<div class="@editContext.FieldCssClass(
() => Model.Identifier)">
<InputText @bind-Value="Model.Identifier" />
<ValidationMessage For="() => Model.Identifier" />
</div>
</EditForm>
```

:::moniker-end

<a id="display-pending-and-faulted-validation-state"></a>
Expand Down
51 changes: 50 additions & 1 deletion aspnetcore/fundamentals/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <xref:System.InvalidOperationException>.

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 <xref:System.ComponentModel.DataAnnotations.RequiredAttribute>, and the cancellation token is passed to the asynchronous operation:
Comment thread
guardrex marked this conversation as resolved.

```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<ValidationResult?> IsValidAsync(
object? value,
ValidationContext validationContext,
CancellationToken cancellationToken)
{
if (value is not string email || string.IsNullOrWhiteSpace(email))
{
return ValidationResult.Success;
}

var userService =
validationContext.GetRequiredService<IUserService>();

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 <xref:System.InvalidOperationException>.

```csharp
Expand Down
Loading