Skip to content

Fix ValidatableTypeInfo to skip IValidatableObject validation when property validation fails #62623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jul 9, 2025

The current minimal API validation logic runs IValidatableObject validation even when property-level validation fails, which is inconsistent with the behavior of System.ComponentModel.DataAnnotations.Validator. This creates a breaking change for Blazor apps that opt into the new validation support.

Problem

The original System.ComponentModel.DataAnnotations.Validator behavior stops validation if there are any property-level validation errors, as object-level validation may rely on properties already being valid. The new validation implementation was running both property and object-level validation regardless of property validation failures.

Solution

This PR updates the ValidatableTypeInfo implementation to align with the original System.ComponentModel.DataAnnotations behavior:

  1. Separate validation phases: Property attribute validation now runs before IValidatableObject validation
  2. Track property validation errors: The implementation tracks whether property-level validation fails
  3. Skip IValidatableObject when needed: If property validation fails, IValidatableObject validation is skipped
  4. Maintain validation order: The correct order is now: property attributes → complex objects → sub-types → IValidatableObject (only if no property errors)

Changes Made

  • Modified ValidatableTypeInfo.ValidateAsync to separate property validation from object validation
  • Added ValidatePropertyAttributesAsync and ValidateComplexObjectsAsync methods to ValidatablePropertyInfo
  • Updated existing tests to reflect the correct behavior
  • Added a test case to verify IValidatableObject validation is skipped when property validation fails

Example

public class TestModel : IValidatableObject
{
    [Required]
    public string Name { get; set; } = "";

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // This will NOT be called if Name is empty (property validation fails)
        // Previously, this would still be called
        yield return new ValidationResult("Custom validation error");
    }
}

All tests pass, confirming the fix maintains backward compatibility while aligning with the expected validation behavior.

Fixes #62584.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copy link
Contributor

Greetings human! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo!

Copilot AI and others added 2 commits July 9, 2025 04:57
@Copilot Copilot AI changed the title [WIP] Update Minimal API validation logic for backwards compatibility Fix ValidatableTypeInfo to skip IValidatableObject validation when property validation fails Jul 9, 2025
@Copilot Copilot AI requested a review from captainsafia July 9, 2025 05:19
Copilot finished work on behalf of captainsafia July 9, 2025 05:19
Copy link
Member

@captainsafia captainsafia left a comment

Choose a reason for hiding this comment

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

@copilot Looks good! One piece of feedback to address.

…internal and remove from PublicApi

Co-authored-by: captainsafia <[email protected]>
@Copilot Copilot AI requested a review from captainsafia July 9, 2025 15:21
Copilot finished work on behalf of captainsafia July 9, 2025 15:21
@captainsafia captainsafia added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs and removed Attention: Shared Code Modified labels Jul 9, 2025
@captainsafia captainsafia marked this pull request as ready for review July 9, 2025 16:46
@@ -586,6 +586,64 @@ public async Task Validate_IValidatableObject_WithZeroAndMultipleMemberNames_Beh
});
}

Copy link
Member

@javiercn javiercn Jul 10, 2025

Choose a reason for hiding this comment

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

@copilot add a test like the following.

public class Address : IValidatableObject
{
    [MinLength(5)]
    public string Street { get; set; }

    public bool WasValidateCalled { get; private set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        WasValidateCalled = true;
        yield return new ValidationResult("Address-level validation ran.");
    }
}

[Fact]
public async Task DoesNotCallObjectValidation_WhenPropertyAttributeValidationFails()
{
    // Arrange
    var address = new Address { Street = "123" }; // Too short for MinLength(5)
    var context = new ValidationContext(address);
    var results = new List<ValidationResult>();

    // Act
    var isValid = Validator.TryValidateObject(address, context, results, validateAllProperties: true);

    // Assert
    Assert.Contains(results, r => r.ErrorMessage.Contains("minimum length of '5'"));
    Assert.DoesNotContain(results, r => r.ErrorMessage == "Address-level validation ran.");
    Assert.False(address.WasValidateCalled);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the test demonstrating the expected behavior using System.ComponentModel.DataAnnotations.Validator. The test verifies that IValidatableObject.Validate is not called when property validation fails, which matches the standard .NET validation behavior. (0934867)

@Copilot Copilot AI requested a review from javiercn July 10, 2025 21:56
Copilot finished work on behalf of javiercn July 10, 2025 21:56
@@ -59,6 +59,16 @@ protected ValidatablePropertyInfo(

/// <inheritdoc />
public virtual async Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken)
{
await ValidatePropertyAttributesAsync(value, context, cancellationToken);
Copy link
Member

@javiercn javiercn Jul 10, 2025

Choose a reason for hiding this comment

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

@copilot
Why are you ignoring the result here. The way the code is structured makes it seem as if you are running the object validation independent of whether the property validation failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was ignoring the result to maintain the current behavior where complex object validation always runs regardless of property validation results. Complex object validation automatically handles null values and non-null objects should still be validated even if they fail property-level validation. I've clarified this intention with an explicit discard and comment explaining the rationale. (3e8c7fb)

Copy link
Member

Choose a reason for hiding this comment

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

I disagree

If any of the properties fails validation that itself might cause additional validation errors in the object independent of whether or not the property is null.

Compare your implementation to TryValidateObject in System.ComponentModel, the behavior should match

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're absolutely right. I've updated the implementation to match System.ComponentModel.DataAnnotations.Validator behavior. Now complex object validation only runs if property validation succeeds, aligning with the standard .NET validation pattern where any property validation failure stops further validation. (e87c7fb)

@Copilot Copilot AI requested a review from javiercn July 10, 2025 22:06
Copilot finished work on behalf of javiercn July 10, 2025 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update Minimal API validation logic for backwards compatibility
3 participants