Skip to content

ValidatableType silent ignore validation for most use cases #64435

@davhdavh

Description

@davhdavh

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

  • Silent ignore validation on visibility issues
  • Silent ignore validation on IValidatableObject on interfaces
  • Silent ignore validation on Validation Attributes on interface properties
  • Silent ignore validation on types used via Interfaces with no Validation Attributes properties
  • Mess with ValidatableType in .Embedded and adding [SkipValidation] which adds a using that then causes a name conflict for ValidatableType.

Expected Behavior

Validation is done, or compile error

Steps To Reproduce

using System.ComponentModel.DataAnnotations;

namespace BlazorApp22.Components.Pages;

/*
//uncomment for compile error
using Microsoft.Extensions.Validation;
   
[ValidatableType]
public class CompileError
{
   [SkipValidation] public TheChild Child { get; } = new();

   public class TheChild
   {
      [Required]
      public string? Name { get; set; }
   }
}
*/

public partial class Home
{
   private PrivateModel PrivateVisibility { get; } = new();
   private InternalModel InternalVisibility { get; } = new();
   private PublicModel PublicVisibility { get; } = new();
   private PublicWithInternalChildModel PublicWithInternalChild { get; } = new();
   private PublicWithInternalChildPropModel PublicWithInternalChildProp { get; } = new();
   private ChildWithIValidatableObject ChildWithIValidatableObject { get; } = new();
   private ChildWithAttribute ChildWithAttribute { get; } = new();
   private ChildWithDoubleValidatableType ChildWithDoubleValidatableType { get; } = new();
   private ChildWithClassWithAttribute ChildWithClassWithAttribute { get; } = new();
   private ChildWithClassWithTrippleAttribute ChildWithClassWithTrippleAttribute { get; } = new();
   private ChildWithClassWithAdditionalProp ChildWithClassWithAdditionalProp { get; } = new();

   /// <summary>
   /// No validation done!
   /// </summary>
   [ValidatableType]
   private class PrivateModel
   {
      public TheChild Child { get; } = new();

      public class TheChild
      {
         [Required] public string? Name { get; set; }
      }
   }

   private void ValidSubmit()
   {
      Console.WriteLine("Failed");
   }
}


/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
internal class InternalModel
{
   public TheChild Child { get; } = new();
   
   public class TheChild
   {
      [Required] public string? Name { get; set; }
   }
}

/// <summary>
/// Required error => Works
/// </summary>
[ValidatableType]
public class PublicModel
{
   public TheChild Child { get; } = new();
   
   public class TheChild
   {
      [Required] public string? Name { get; set; }
   }
}

/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class PublicWithInternalChildModel
{
   internal TheChild Child { get; } = new();
   
   public class TheChild
   {
      [Required] public string? Name { get; set; }
   }
}
/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class PublicWithInternalChildPropModel
{
   public TheChild Child { get; } = new();
   
   public class TheChild
   {
      [Required] internal string? Name { get; set; }
   }
}
/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class ChildWithIValidatableObject
{
   public IChild Child { get; } = new TheChild();
   
   public interface IChild : IValidatableObject
   {
      public string? Name { get; set; }
   }

   public class TheChild : IChild
   {
      public string? Name { get; set; }

      public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) => [new("Custom", [nameof(Name)])];
   }
}

/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class ChildWithAttribute
{
   public IChild Child { get; } = new TheChild();
   
   public interface IChild
   {
      [Required] public string? Name { get; set; }
   }

   public class TheChild : IChild
   {
      public string? Name { get; set; }
   }
}

/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class ChildWithDoubleValidatableType
{
   public IChild Child { get; } = new TheChild();
   
   public interface IChild
   {
      [Required] public string? Name { get; set; }
   }

   [ValidatableType]
   public class TheChild : IChild
   {
      public string? Name { get; set; }
   }
}

/// <summary>
/// No validation done!
/// </summary>
[ValidatableType]
public class ChildWithClassWithAttribute
{
   public IChild Child { get; } = new TheChild();
   public interface IChild
   {
      public string? Name { get; set; }
   }

   [ValidatableType]
   public class TheChild : IChild, IValidatableObject
   {
      [Required] public string? Name { get; set; }
      [Required] public string? Name2 { get; set; }
    
      public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) => [new("Custom", [nameof(Name)])];
   }

}


/// <summary>
/// Required error => Works
/// </summary>
[ValidatableType]
public class ChildWithClassWithTrippleAttribute
{
   public IChild Child { get; } = new TheChild();
   public interface IChild
   {
      [Required] public string? Name { get; set; }
   }

   [ValidatableType]
   public class TheChild : IChild
   {
      [Required] public string? Name { get; set; }
   }

}


/// <summary>
/// Required error => Works
/// </summary>
[ValidatableType]
public class ChildWithClassWithAdditionalProp
{
   public IChild Child { get; } = new TheChild();
   public interface IChild
   {
      [Required] public bool Dummy { get; }
      public string? Name { get; set; }
   }

   [ValidatableType]
   public class TheChild : IChild
   {
      public bool Dummy => true;
      [Required] public string? Name { get; set; }
   }

}
@page "/"
@rendermode InteractiveServer
<PageTitle>Home</PageTitle>

Leave the value empty and press submit! They should all give a required error!!

<EditForm Model="PrivateVisibility" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator/>
 <label>
  PrivateVisibility
  <InputText @bind-Value="@PrivateVisibility.Child.Name"/>
 </label>
 <ValidationMessage For="() => PrivateVisibility.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

<EditForm Model="InternalVisibility" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  InternalVisibility
  <InputText @bind-Value="@InternalVisibility.Child.Name" />
 </label>
 <ValidationMessage For="() => InternalVisibility.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>


<EditForm Model="PublicVisibility" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  PublicVisibility
  <InputText @bind-Value="@PublicVisibility.Child.Name" />
 </label>
 <ValidationMessage For="() => PublicVisibility.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>


<EditForm Model="PublicWithInternalChild" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  PublicWithInternalChild
  <InputText @bind-Value="@PublicWithInternalChild.Child.Name" />
 </label>
 <ValidationMessage For="() => PublicWithInternalChild.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>



<EditForm Model="PublicWithInternalChildProp" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  PublicWithInternalChildProp
  <InputText @bind-Value="@PublicWithInternalChildProp.Child.Name" />
 </label>
 <ValidationMessage For="() => PublicWithInternalChildProp.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>



<EditForm Model="ChildWithIValidatableObject" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  ChildWithIValidatableObject
  <InputText @bind-Value="@ChildWithIValidatableObject.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithIValidatableObject.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

<EditForm Model="ChildWithAttribute" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  ChildWithAttribute
  <InputText @bind-Value="@ChildWithAttribute.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithAttribute.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

<EditForm Model="ChildWithDoubleValidatableType" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  ChildWithDoubleValidatableType
  <InputText @bind-Value="@ChildWithDoubleValidatableType.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithDoubleValidatableType.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

<EditForm Model="ChildWithClassWithAttribute" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  ChildWithClassWithAttribute
  <InputText @bind-Value="@ChildWithClassWithAttribute.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithClassWithAttribute.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>
<EditForm Model="ChildWithClassWithTrippleAttribute" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <label>
  ChildWithClassWithTrippleAttribute
  <InputText @bind-Value="@ChildWithClassWithTrippleAttribute.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithClassWithTrippleAttribute.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

<EditForm Model="ChildWithClassWithAdditionalProp" OnValidSubmit="ValidSubmit">
 <DataAnnotationsValidator />
 <ValidationSummary/>
 <label>
  ChildWithClassWithAdditionalProp
  <InputText @bind-Value="@ChildWithClassWithAdditionalProp.Child.Name" />
 </label>
 <ValidationMessage For="() => ChildWithClassWithAdditionalProp.Child.Name"></ValidationMessage>
 <button type="submit">Submit</button>
</EditForm>

and ofcrouse builder.Services.AddValidation(); in program.cs

Exceptions (if any)

No response

.NET Version

10.0.100

Anything else?

Visual Studio 2026 Developer PowerShell v18.3.0-insiders

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templates

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions