Open anttikes opened 3 years ago
Tagging subscribers to this area: @ajcvickers, @bricelam, @roji See info in area-owners.md if you want to be subscribed.
Author: | anttikes |
---|---|
Assignees: | - |
Labels: | `area-System.ComponentModel.DataAnnotations`, `untriaged` |
Milestone: | - |
I am already aware of one common workaround, and it is to implement IValidatableObject on the request class, and then in the body of the validation method to manually instantiate each of the validation attributes and provide the "value to be validated" from the Value property of the Optional record.
This workaround works but from the code perspective it is just plain ugly.
I also found out that not all of the "string"-based built-in validation attributes follow a similar suite. For example:
It is unclear why there's a difference in behavior between the attributes. Logically thinking they should all follow the same approach.
Description
Validation attributes such as EmailAddressAttribute make use of the "is" keyword. The problem with this keyword is that it completely ignores user-defined conversion operators.
The following example comes from our own implementation where we support JSON Merge Patch:
The OptionalConverterFactory mentioned in the code, and the generic converter class behind it are not relevant to this discussion as they have already been verified to work correctly.
The problem comes when we attempt to use this new record type with the existing validation attributes. Consider the following request class:
And a Json-based "merge patch" request coming in to an ASP.NET Core Web API:
The result is that the validation always fails because the EmailAddressAttribute uses the 'is' keyword. This keyword completely ignores the user-provided conversion operator, and thus line 25 of the attribute's validation code will always return false.
Expected behavior
I would expect the validation attributes work correctly even when wrappers are used. Ironically, the 'is' operator is hard-wired to support the Nullable struct but unfortunately for us, Nullable only allows value type parameters. So, we cannot use it as a replacement for the Optional record which allows both value and reference types.
We cannot "do away" without the Optional class as there doesn't seem to be a way to distinguish between "value was not provided" and "value was set to null", especially when using the System.String type.
Configuration
.NET Version: .NET Core v5.0.9 OS info: Windows 10, with all the latest Windows Update patches Architecture: x64
Analysis suggests that this issue is not specific to the OS or architecture configuration. The attribute's validation code is the same everywhere.
Regression?
Not a regression. The .NET Framework counterpart uses the 'as' keyword which suffers from the same problem.