Implementing async validation logic should not require implementing sync IsValid
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
To write an async validation rule — say a uniqueness check against a database — an author derives from `AsyncValidationAttribute` and implements `IsValidAsync`. But `IsValid(object?, ValidationContext)` is also `abstract`, so they must implement a synchronous method too:
```
error CS0534: 'UniqueUsernameAttribute' does not implement inherited abstract member
'AsyncValidationAttribute.IsValid(object?, ValidationContext)'
```
That method is usually irrelevant to what the author is trying to express. Their rule needs I/O; there's nothing meaningful for the sync overload to do. So it becomes a required stub whose correct contents aren't obvious.
### The choice is confusing and error-prone
How should a user implement `IsValid`, and why does `AsyncValidationAttribute` require it at all? `AsyncValidationAttribute` is for defining async validation logic; for sync logic there is already `ValidationAttribute`. It derives from `ValidationAttribute` so that it can influence what happens when only the synchronous validation path is called. The framework's own validators could detect that case and offer configurable behavior, but these attributes may also be consumed by validation systems the framework doesn't control — `attribute.IsValid(...)` and `attribute.GetValidationResult(...)` are public entry points that bypass `Validator` entirely, so the behavior has to live on the attribute itself.
This is not a rare path. Existing DataAnnotations-based validation systems are synchronous only; asynchronous validation is new in .NET 11 and opt-in. So whatever an author writes in `IsValid` is what essentially every existing consumer of that attribute will execute today. The synchronous overload is the common case at the moment, not the fallback.
In most cases the right behavior is to throw. Running sync-only validation when async validation is required is a programming error, and the alternatives are both worse:
- **Silently passing** means the rule doesn't run at all, which is a security risk.
- **Failing** is also wrong, because there is no reasonable message to show the user — they have no control over whether validation was invoked synchronously or asynchronously.
This does mean a system running synchronous validation will hit an exception when handed a newer type that requires async validation. That is the correct outcome: running sync validation on a type that requires async validation is invalid, and silently ignoring the validation logic would be a security risk. It is also worth noting that leaving `IsValid` to the author does not avoid this — it only makes the result unpredictable. Since existing systems are sync-only, they are the ones that will execute whatever each author wrote, so the behavior they see is currently outside the framework's control.
Requiring users to implement `IsValid` themselves pushes them toward two bad shapes:
- **Sync-over-async.** Users will try to implement `IsValid` in terms of the async logic. This may work in limited cases, but in environments like ASP.NET Core it can cause thread pool starvation.
- **Returning success.** Users may assume both sync and async validation always run, and implement `IsValid` to always return valid. I've seen AI coding assistants make exactly this assumption.
### The same applies to `IAsyncValidatableObject`
`IAsyncValidatableObject` derives from `IValidatableObject` and declares only `ValidateAsync`, so implementing async object-level validation likewise requires supplying a synchronous `Validate` with the same unhelpful choice of contents.
### Proposal
Make `AsyncValidationAttribute.IsValid` virtual, with a default implementation that throws to indicate synchronous validation isn't supported for the attribute. Give `IAsyncValidatableObject.Validate` an equivalent default interface implementation.
- Authors writing async validation implement only the async member.
- Authors who have a correct synchronous equivalent can still override it.
- Existing sync-only validation systems get one predictable behavior instead of whatever each author happened to write.
### Related
- #130719 (api-approved) extends the same requirement to `IAsyncValidateOptions`, citing `AsyncValidationAttribute` as the precedent to follow — so this is worth settling now.
This issue is about the authoring requirement only, not whether a synchronous contract should exist.
_Observed on `11.0.0-preview.6.26359.118`._
Contributor guide
Assessment
This issue has not been assessed yet.