Skip to content

Move the abstract Validatable*Info from Microsoft.Extensions.Validation to be source-generated#67956

Merged
Youssef1313 merged 19 commits into
mainfrom
dev/ygerges/val-refactor
Jul 23, 2026
Merged

Move the abstract Validatable*Info from Microsoft.Extensions.Validation to be source-generated#67956
Youssef1313 merged 19 commits into
mainfrom
dev/ygerges/val-refactor

Conversation

@Youssef1313

@Youssef1313 Youssef1313 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Fixes #67960

See the linked issue for all the details on why we are making these changes. Copying some highlights:

  • We want to avoid having the abstract Validatable[Type|Property|Parameter]Info in the public API. So we are moving them to be source-generated as file classes in user assemblies.
  • We are dropping the event because it's not really needed for Blazor, it's adding complexity to the implementation, and will make it harder (if possible at all) to keep the classes source-generated.
  • We are changing the ValidationErrors dictionary to include the ValidationError. This includes all the info that Blazor needs and is how we are able to delete the OnValidationError event.

@oroztocil oroztocil left a comment

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.

Only few minor comments. When the tests are fixed this should be solid. Run a few samples manually before merging, please (or let me know if you want help there).

Comment thread src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs Outdated
Comment thread src/Validation/src/DisplayNameInfo.cs Outdated
@Youssef1313
Youssef1313 marked this pull request as ready for review July 22, 2026 15:00
Copilot AI review requested due to automatic review settings July 22, 2026 15:00
@Youssef1313
Youssef1313 requested review from a team, BrennanConroy and halter73 as code owners July 22, 2026 15:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@Youssef1313

Copy link
Copy Markdown
Member Author

@copilot Tests PropertyDisplayName_DisplayAttributeTakesPrecedenceOverDisplayNameAttribute and PropertyDisplayName_DisplayAttributeResourceTypeTakesPrecedenceOverDisplayNameAttribute are failing. Update the snapshots so that they pass.

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@copilot Tests PropertyDisplayName_DisplayAttributeTakesPrecedenceOverDisplayNameAttribute and PropertyDisplayName_DisplayAttributeResourceTypeTakesPrecedenceOverDisplayNameAttribute are failing. ...

Updated both snapshot files. The issue was that a recent commit removed #pragma warning disable ASP0029 from the generated output but these two snapshots were missed. They also had a UTF-8 BOM character and were missing trailing newlines. Fixed in this commit.

@Youssef1313 Youssef1313 changed the title Microsoft.Extensions.Validation refactor Move the abstract Validatable*Info from Microsoft.Extensions.Validation to be source-generated Jul 22, 2026
@Youssef1313

Copy link
Copy Markdown
Member Author

@copilot There are additional test failures in ValidationEndpointFilterFactoryTests. For example:

2026-07-22T17:44:54.3218406Z [xUnit.net 00:00:04.13]     Microsoft.AspNetCore.Http.Extensions.Tests.ValidationEndpointFilterFactoryTests.ValidatesBindAsyncParameter_CurrentBehavior [FAIL]
2026-07-22T17:44:54.3218616Z [xUnit.net 00:00:04.13]       Assert.Equal() Failure: Values differ
2026-07-22T17:44:54.3218652Z [xUnit.net 00:00:04.13]       Expected: 400
2026-07-22T17:44:54.3218715Z [xUnit.net 00:00:04.13]       Actual:   200

Investigate that.

Copilot AI and others added 2 commits July 22, 2026 18:58
Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>
…e resolver in AddValidation

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@copilot There are additional test failures in ValidationEndpointFilterFactoryTests. For example:

...

Fixed in the latest commit. The root cause was that AddValidation() no longer registered a RuntimeValidatableParameterInfoResolver after the source-generator changes, so projects without the source generator configured (like Microsoft.AspNetCore.Http.Extensions.Tests) had no resolver to handle parameter validation.

The fix creates an internal RuntimeValidatableParameterInfoResolver in the library (src/Validation/src/RuntimeValidatableParameterInfoResolver.cs) with the full reflection-based validation logic, and restores its registration in AddValidation(). All 14 ValidationEndpointFilterFactoryTests now pass.

@Youssef1313

Copy link
Copy Markdown
Member Author

@copilot It's expected that users will have the source generator available. Revert 1a52a50

…g runtime resolver in AddValidation"

This reverts commit 1a52a50.

Users are expected to have the source generator available; restoring the source generator reference to the test project instead.

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@copilot It's expected that users will have the source generator available. Revert 1a52a50

Done. Reverted in commit 57e1ff3. The RuntimeValidatableParameterInfoResolver is removed along with its registration in AddValidation(), and the source generator reference is restored in the Http.Extensions.Tests project.

Comment thread src/Validation/gen/Templates/ValidatableTypeInfo.cs Outdated
oroztocil and others added 2 commits July 23, 2026 01:43
Address API feedback on #67960: ValidationError carried a plural Errors
list that the generator always populated with a single message, while
AddValidationError already groups errors by Path. Collapse the redundant
axis to a scalar string ErrorMessage and rely on the per-path list.

Also make ValidationError a sealed class instead of a readonly struct so
default(ValidationError) no longer bypasses the required members and hands
back null Name/Path/ErrorMessage. It is only materialized on validation
failure, so the extra allocation is not a concern.

Update the generator templates, the Blazor EditContext integration, the
minimal API endpoint filter, and all affected tests and snapshots.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 762782cd-5621-4641-8bea-ecc5675c0dbf
…n from generated resolvers

Address PR review feedback and fix the Http.Extensions.Tests CI build break.

- ValidationContext for type-level validation now uses `value ?? _throwawayObjectInstance`
  instead of always passing the throwaway placeholder, so IValidatableObject.Validate (sync
  and async) receives the model being validated as ObjectInstance. Previously user code that
  cast ObjectInstance back to the model threw. Adds a regression test covering both paths.

- The generated file-local resolver/type-info implementations no longer emit [NotNullWhen] on
  their out parameters. The public interfaces (IValidatableInfoResolver, IValidatableTypeInfo)
  keep the attribute, so callers are unaffected, but the emitted code no longer references
  System.Diagnostics.CodeAnalysis.NotNullWhenAttribute. That reference was ambiguous (CS0433)
  in compilations where another source generator (e.g. the RequestDelegateGenerator) contributes
  an internal polyfill of the same attribute via InternalsVisibleTo, which broke the build of
  Microsoft.AspNetCore.Http.Extensions.Tests. CS8767 on the implementations is suppressed in the
  generated file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 762782cd-5621-4641-8bea-ecc5675c0dbf
@Youssef1313

Youssef1313 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Possibly flaky test:

---- Assert.True() Failure
Expected: True
Actual:   False
  Stack Trace:
     at Microsoft.AspNetCore.E2ETesting.WaitAssert.WaitAssertCore[TResult](IWebDriver driver, Func`1 assertion, TimeSpan timeout) in /mnt/vss/_work/1/s/src/Shared/E2ETesting/WaitAssert.cs:line 160
   at Microsoft.AspNetCore.E2ETesting.WaitAssert.WaitAssertCore(IWebDriver driver, Action assertion, TimeSpan timeout) in /mnt/vss/_work/1/s/src/Shared/E2ETesting/WaitAssert.cs:line 93
   at Microsoft.AspNetCore.E2ETesting.WaitAssert.True(IWebDriver driver, Func`1 actual, TimeSpan timeout) in /mnt/vss/_work/1/s/src/Shared/E2ETesting/WaitAssert.cs:line 38
   at Microsoft.AspNetCore.Components.E2ETest.Tests.VirtualizationTest.WindowScrollMidListAndWaitForRender(IJavaScriptExecutor js) in /mnt/vss/_work/1/s/src/Components/test/E2ETest/Tests/VirtualizationTest.cs:line 4370
   at Microsoft.AspNetCore.Components.E2ETest.Tests.VirtualizationTest.AnchorMode_WindowScroll_HomeKeyJumpsToTop(String anchorMode) in /mnt/vss/_work/1/s/src/Components/test/E2ETest/Tests/VirtualizationTest.cs:line 4658
   at InvokeStub_VirtualizationTest.AnchorMode_WindowScroll_HomeKeyJumpsToTop(Object, Span`1)

Build: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1522734&view=logs&jobId=9d712d69-d7e9-5af2-8014-b7b000c80b8b

FYI @ilonatommy

@Youssef1313
Youssef1313 enabled auto-merge (squash) July 23, 2026 08:44
@Youssef1313
Youssef1313 merged commit 0ae3b02 into main Jul 23, 2026
26 checks passed
@Youssef1313
Youssef1313 deleted the dev/ygerges/val-refactor branch July 23, 2026 08:45
@Youssef1313

Copy link
Copy Markdown
Member Author

/backport to release/11.0-preview7

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0-preview7 (link to workflow run)

@github-actions

Copy link
Copy Markdown
Contributor

@Youssef1313 an error occurred while backporting to release/11.0-preview7. See the workflow output for details.

Copilot AI added a commit that referenced this pull request Jul 23, 2026
…tion to be source-generated (#67956)

* Refactor validation

* Step 2

* Refactor

* Move to SG

* Introduce

* Rename

* Snapshots, rename

* Generated

* Namespace

* Reduce comments

* Avoid usings. Use global::

* Stable

* Remove unused usings

* Update failing snapshot tests to remove BOM and pragma

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Initial plan: fix ValidationEndpointFilterFactory test failures

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Fix ValidationEndpointFilterFactory test failures by restoring runtime resolver in AddValidation

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Revert "Fix ValidationEndpointFilterFactory test failures by restoring runtime resolver in AddValidation"

This reverts commit 1a52a50.

Users are expected to have the source generator available; restoring the source generator reference to the test project instead.

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Simplify ValidationError to a single ErrorMessage and make it a class

Address API feedback on #67960: ValidationError carried a plural Errors
list that the generator always populated with a single message, while
AddValidationError already groups errors by Path. Collapse the redundant
axis to a scalar string ErrorMessage and rely on the per-path list.

Also make ValidationError a sealed class instead of a readonly struct so
default(ValidationError) no longer bypasses the required members and hands
back null Name/Path/ErrorMessage. It is only materialized on validation
failure, so the extra allocation is not a concern.

Update the generator templates, the Blazor EditContext integration, the
minimal API endpoint filter, and all affected tests and snapshots.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 762782cd-5621-4641-8bea-ecc5675c0dbf

* Pass the validated instance to IValidatableObject and drop NotNullWhen from generated resolvers

Address PR review feedback and fix the Http.Extensions.Tests CI build break.

- ValidationContext for type-level validation now uses `value ?? _throwawayObjectInstance`
  instead of always passing the throwaway placeholder, so IValidatableObject.Validate (sync
  and async) receives the model being validated as ObjectInstance. Previously user code that
  cast ObjectInstance back to the model threw. Adds a regression test covering both paths.

- The generated file-local resolver/type-info implementations no longer emit [NotNullWhen] on
  their out parameters. The public interfaces (IValidatableInfoResolver, IValidatableTypeInfo)
  keep the attribute, so callers are unaffected, but the emitted code no longer references
  System.Diagnostics.CodeAnalysis.NotNullWhenAttribute. That reference was ambiguous (CS0433)
  in compilations where another source generator (e.g. the RequestDelegateGenerator) contributes
  an internal polyfill of the same attribute via InternalsVisibleTo, which broke the build of
  Microsoft.AspNetCore.Http.Extensions.Tests. CS8767 on the implementations is suppressed in the
  generated file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 762782cd-5621-4641-8bea-ecc5675c0dbf

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ondrej Roztocil <roztocil@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 762782cd-5621-4641-8bea-ecc5675c0dbf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microsoft.Extensions.Validation API stabilization for .NET 11

6 participants