Skip to content

Add async validation support for System.ComponentModel.DataAnnotations#128656

Open
ViveliDuCh wants to merge 6 commits into
dotnet:mainfrom
ViveliDuCh:async-validation-dataannotations
Open

Add async validation support for System.ComponentModel.DataAnnotations#128656
ViveliDuCh wants to merge 6 commits into
dotnet:mainfrom
ViveliDuCh:async-validation-dataannotations

Conversation

@ViveliDuCh
Copy link
Copy Markdown
Member

Fixes #128096

Implements async validation support for System.ComponentModel.DataAnnotations as approved in API review.

What's included

  • AsyncValidationAttribute — abstract base class deriving from ValidationAttribute for async validation scenarios (database lookups, API calls). IsValid is abstract override so subclasses must decide how to handle synchronous callers. IsValidAsync is the primary async entry point. GetValidationResultAsync mirrors the sync GetValidationResult with error message formatting.
  • IAsyncValidatableObject — interface extending IValidatableObject for object-level async validation via IAsyncEnumerable<ValidationResult>. No default interface method — implementors provide both Validate and ValidateAsync.
  • Eight async Validator methods — TryValidateObjectAsync (2 overloads), TryValidatePropertyAsync, TryValidateValueAsync, ValidateObjectAsync (2 overloads), ValidatePropertyAsync, ValidateValueAsync. All return Task/Task<bool>.
  • Ref assembly updates for the full async API surface
  • ValidationContext.Items XML doc remark about thread safety under concurrent async validation
  • Tests covering all async Validator methods, mixed sync/async attributes, breakOnFirstError with parallel async, cancellation propagation, IAsyncValidatableObject, class-level async attributes, error message formatting, and sync fallback via IsValid override

Implementation notes

  • The async pipeline follows a 3-step approach matching the sync structure: property validation → type attributes → IAsyncValidatableObject/IValidatableObject
  • Property validation runs in parallel using Task.WhenAny with a linked CancellationTokenSource for cooperative breakOnFirstError cancellation
  • Per-value validation is two-phase: sync attributes first (abort early on errors), then async attributes in parallel
  • breakOnFirstError behavior is consistent between sync and async paths

API review decisions reflected

  • Task/Task<T> return types (not ValueTask)
  • IsValid as abstract override
  • No default interface method on IAsyncValidatableObject

Implements the approved API shape from dotnet#128096.

- Add AsyncValidationAttribute with abstract IsValidAsync returning
  Task<ValidationResult?> and abstract override IsValid for sync fallback.
- Add IAsyncValidatableObject extending IValidatableObject with
  ValidateAsync returning IAsyncEnumerable<ValidationResult>; no DIM,
  implementors must provide both Validate and ValidateAsync explicitly.
- Add 8 Validator.*Async methods (TryValidateObject/Property/Value,
  ValidateObject/Property/Value) all returning Task/Task<bool>.
- Async pipeline validates properties in parallel using Task.WhenAny
  with linked CancellationTokenSource for cooperative breakOnFirstError
  cancellation, matching sync behavior parity.
- Update ref assembly, csproj, and ValidationContext.Items XML docs.
- Add comprehensive tests covering all async methods, cancellation,
  breakOnFirstError, mixed sync/async attributes, and IAsyncValidatableObject.
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-componentmodel-dataannotations
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new async validation surface to System.ComponentModel.DataAnnotations by introducing AsyncValidationAttribute, IAsyncValidatableObject, and async counterparts to the Validator APIs, plus tests and a small documentation update for ValidationContext.Items.

Changes:

  • Add AsyncValidationAttribute and IAsyncValidatableObject as new public types to support async validation patterns.
  • Add async Validator APIs and implement an async validation pipeline with parallel execution for property/async-attribute validation.
  • Add unit tests for the new async APIs and update ValidationContext.Items docs for parallel async validation scenarios.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs Implements async Validator APIs and async/parallel validation pipeline.
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/AsyncValidationAttribute.cs Introduces base type for async validation attributes with IsValidAsync + GetValidationResultAsync.
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/IAsyncValidatableObject.cs Introduces async object-level validation contract (ValidateAsync).
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs Documents Items thread-safety expectations under parallel async validation.
src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs Adds new public API surface to the ref assembly (needs ordering consistency fix).
src/libraries/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj Adds new source files to the project.
src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidatorTests.cs Adds tests for async Validator APIs (includes timing-based parallelism assertions).
src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationAttributeTests.cs Adds tests for AsyncValidationAttribute behaviors (sync fallback vs async path, formatting, cancellation).

…timing-based parallelism tests with deterministic concurrency probes
…ssage into base class, use constructed generic types in XML doc crefs, and add missing exception documentation
Copilot AI review requested due to automatic review settings May 28, 2026 17:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

…drain remaining tasks in finally to avoid leaks on exception paths, and clarify RequiresValidationContext scope in XML docs
Copilot AI review requested due to automatic review settings May 29, 2026 04:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

ViveliDuCh added a commit to ViveliDuCh/runtime that referenced this pull request May 29, 2026
- Add IAsyncValidateOptions<T> and IAsyncStartupValidator interfaces
- Add AsyncValidateOptions<T, TDep1..TDep5> lambda-based validators
- Add async Validate overloads on OptionsBuilder<T> (0-5 dependencies)
- Extend ValidateOnStart to prefer IAsyncStartupValidator when available
- Add DataAnnotationValidateOptionsAsync<T> and ValidateDataAnnotationsAsync extension
- Wire IAsyncStartupValidator into Host.StartAsync
ViveliDuCh added a commit to ViveliDuCh/runtime that referenced this pull request May 29, 2026
- Add IAsyncValidateOptions<T> and IAsyncStartupValidator interfaces
- Add AsyncValidateOptions<T, TDep1..TDep5> lambda-based validators
- Add async Validate overloads on OptionsBuilder<T> (0-5 dependencies)
- Extend ValidateOnStart to prefer IAsyncStartupValidator when available
- Add DataAnnotationValidateOptionsAsync<T> and ValidateDataAnnotationsAsync extension
- Wire IAsyncStartupValidator into Host.StartAsync
Copy link
Copy Markdown
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

/// Provides a way for an object to be validated asynchronously.
/// Inherits from <see cref="IValidatableObject"/>. Implementors must provide both
/// <see cref="IValidatableObject.Validate"/> and <see cref="ValidateAsync"/>.
/// </summary>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth documenting the dispatch contract explicitly here — there's already a test (TryValidateObjectAsync_IAsyncValidatableObject_Preferred_Over_IValidatableObject) asserting this behavior, but a user implementing both methods on the same type has no way to know Validate will be skipped on the async path without reading the Validator source.

Suggested change
/// </summary>
/// <summary>
/// Provides a way for an object to be validated asynchronously.
/// Inherits from <see cref="IValidatableObject"/>. Implementors must provide both
/// <see cref="IValidatableObject.Validate"/> and <see cref="ValidateAsync"/>.
/// </summary>
/// <remarks>
/// When an object implements both <see cref="IAsyncValidatableObject"/> and
/// <see cref="IValidatableObject"/>, the asynchronous <see cref="Validator"/> APIs
/// (such as <see cref="Validator.TryValidateObjectAsync(object, ValidationContext, System.Collections.Generic.ICollection{ValidationResult}?, System.Threading.CancellationToken)"/>)
/// invoke only <see cref="ValidateAsync"/>; <see cref="IValidatableObject.Validate"/>
/// is not called as part of the async pipeline. The synchronous <see cref="Validator"/>
/// APIs continue to invoke <see cref="IValidatableObject.Validate"/>.
/// </remarks>

Copy link
Copy Markdown
Member

@oroztocil oroztocil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Async Validation Support for System.ComponentModel.DataAnnotations

5 participants