| File: System\ComponentModel\DataAnnotations\AsyncValidationAttribute.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.Annotations\src\System.ComponentModel.Annotations.csproj (System.ComponentModel.Annotations) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; using System.Threading.Tasks; namespace System.ComponentModel.DataAnnotations { /// <summary> /// Base class for validation attributes that require asynchronous operations, such as database lookups or API calls. /// Derived implementations must be thread-safe, as instances of this attribute may be invoked concurrently from multiple threads. /// </summary> public abstract class AsyncValidationAttribute : ValidationAttribute { /// <summary> /// Default constructor for any async validation attribute. /// </summary> protected AsyncValidationAttribute() { } /// <summary> /// Constructor that accepts a fixed validation error message. /// </summary> /// <param name="errorMessage">A non-localized error message to use in <see cref="ValidationAttribute.ErrorMessageString" />.</param> protected AsyncValidationAttribute(string errorMessage) : base(errorMessage) { } /// <summary> /// Allows for providing a resource accessor function that will be used by the <see cref="ValidationAttribute.ErrorMessageString" /> /// property to retrieve the error message. /// </summary> /// <param name="errorMessageAccessor">The <see cref="Func{T}" /> that will return an error message.</param> protected AsyncValidationAttribute(Func<string> errorMessageAccessor) : base(errorMessageAccessor) { } /// <summary> /// Override of the base class <see cref="ValidationAttribute.IsValid(object?, ValidationContext)" /> method. /// Subclasses must provide a synchronous validation implementation or throw an appropriate exception /// to indicate that synchronous validation is not supported. /// </summary> /// <param name="value">The value to validate.</param> /// <param name="validationContext"> /// A <see cref="ValidationContext" /> instance that provides context about the validation operation, /// such as the object and member being validated. Provides access to services required to perform /// validation using <see cref="IServiceProvider" />. /// </param> /// <returns> /// <see cref="ValidationResult.Success" /> when validation is valid. /// An instance of <see cref="ValidationResult" /> when validation is invalid. /// </returns> protected abstract override ValidationResult? IsValid(object? value, ValidationContext validationContext); /// <summary> /// Override this method in subclasses to implement asynchronous validation logic. /// </summary> /// <param name="value">The value to validate.</param> /// <param name="validationContext"> /// A <see cref="ValidationContext" /> instance that provides context about the validation operation, /// such as the object and member being validated. Provides access to services required to perform /// validation using <see cref="IServiceProvider" />. /// </param> /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns> /// A <see cref="Task{ValidationResult}" /> representing the asynchronous validation operation. /// When validation is valid, the result is <see cref="ValidationResult.Success" />. /// When validation is invalid, the result is an instance of <see cref="ValidationResult" />. /// </returns> protected abstract Task<ValidationResult?> IsValidAsync( object? value, ValidationContext validationContext, CancellationToken cancellationToken); /// <summary> /// Sealed override of <see cref="ValidationAttribute.IsValid(object?)" /> that delegates to the /// <see cref="ValidationContext" /> overload so that <see cref="AsyncValidationAttribute" /> implementations /// only need to provide a single synchronous fallback via /// <see cref="ValidationAttribute.IsValid(object?, ValidationContext)" />. /// </summary> /// <param name="value">The value to validate.</param> /// <returns> /// <see langword="true" /> if the value is valid; otherwise, <see langword="false" />. /// </returns> public sealed override bool IsValid(object? value) => IsValid(value, null!) == ValidationResult.Success; /// <summary> /// Tests whether the given <paramref name="value" /> is valid asynchronously with respect to the current /// validation attribute without throwing a <see cref="ValidationException" />. /// </summary> /// <param name="value">The value to validate.</param> /// <param name="validationContext"> /// A <see cref="ValidationContext" /> instance that provides context about the validation operation, /// such as the object and member being validated. Provides access to services required to perform /// validation using <see cref="IServiceProvider" />. /// </param> /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns> /// A <see cref="Task{ValidationResult}" /> representing the asynchronous validation operation. /// When validation is valid, the result is <see cref="ValidationResult.Success" />. /// When validation is invalid, the result is an instance of <see cref="ValidationResult" />. /// </returns> /// <exception cref="InvalidOperationException"> is thrown if the current attribute is malformed.</exception> /// <exception cref="ArgumentNullException">When <paramref name="validationContext" /> is null.</exception> public async Task<ValidationResult?> GetValidationResultAsync( object? value, ValidationContext validationContext, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(validationContext); ValidationResult? result = await IsValidAsync(value, validationContext, cancellationToken).ConfigureAwait(false); return EnsureValidationResultErrorMessage(result, validationContext); } } }