简体   繁体   中英

Skip all the validation of a model base on one of its property (ASP.Net Core 2.0)

How I can skip all the validation of a model base on one of its property. For instance, if the WillBeDeleted property of the object is true the validator skips all the validation rules.

namespace ViewModel
{
    public class ContactForm : IViewModel
    {
        [ValidateNever]
        public Contact Item { get; set; }

        public Category Category { get; set; }

        [Required, DisplayName("First name")]
        [StringLength(200, ErrorMessage = "First name should not exceed 200 characters.")]
        public string FirstName { get; set; }

        [Required, DisplayName("Last name")]
        [StringLength(200, ErrorMessage = "Last name should not exceed 200 characters.")]
        public string LastName { get; set; }

        public List<TelephonesSubForm> Telephones { get; set; } = new List<TelephonesSubForm>();

        public List<EmailsSubForm> Emails { get; set; } = new List<EmailsSubForm>();

        public class TelephonesSubForm : IViewModel
        {
            public int Id { get; set; }

            public bool MustBeDeleted { get; set; }

            [Required]
            public TelephoneAndEmailType Type { get; set; }

            [Required]
            [StringLength(200, MinimumLength = 10, ErrorMessage = "Telephone should not exceed 200 characters.")]
            public string Telephone { get; set; }
        }

        public class EmailsSubForm
        {
            public int Id { get; set; }

            public bool MustBeDeleted { get; set; }

            [Required]
            public TelephoneAndEmailType Type { get; set; }

            [Required]
            [StringLength(200, MinimumLength = 10, ErrorMessage = "Email should not exceed 200 characters.")]
            public string Email { get; set; }
        }
    }
}

In the given sample model, when the MustBeDeleted is true it means that the item of email or telephone will be deleted on the save action.

I have searched and found that several questions about conditional (custom) validation and they are suggesting to remove the specific keys from the ModelState before checking the validation state of it. Like this one.

I'm adding a New answer and keep the old as it could be usefull to someone

So, Create a Custom Attribute:

  public class CustomShouldValidateAttribute : Attribute, IPropertyValidationFilter
  {
    public CustomShouldValidateAttribute(){

    }
    public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry)
    {
      //Here you change this verification to Wether it should verificate or not, In this case we are checking if the property is ReadOnly (Only a get method)
      return !entry.Metadata.IsReadOnly;      
    }
  }

and them on your class you decorate it with your custom attribute

[CustomShouldValidateAttribute]
public class MyClass{
public string Stuff{get;set;}
public string Name {get{return "Furlano";}}
}

Although It is not the complete answer for the question, It is possible to inherit validation attributes ( RequiredAttribute , StringLength , etc ) and override the IsValid method. As a sample, I provide one of them.

public class RequiredWhenItIsNotDeletingAttribute : RequiredAttribute
{
    string DeletingProperty;

    /// <summary>
    /// Check if the object is going to be deleted skip the validation.
    /// </summary>
    /// <param name="deletingProperty">The boolean property which shows the object will be deleted.</param>
    public RequiredWhenItIsNotDeletingAttribute(string deletingProperty = "MustBeDeleted") =>
        DeletingProperty = deletingProperty;

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(DeletingProperty);

        if((bool)property.GetValue(validationContext.ObjectInstance))
            return ValidationResult.Success;

        return base.IsValid(value, validationContext);
    }
}

Inherit your model from IValidatableObject

and implement the interface method:

  public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {

      if (YourCondition==false){
        yield return new ValidationResult(
          $"This is wrong",
          new[] { nameof(Email) });
      }
      else
         yield return ValidationResult.Success

    }

But you will have to implemment a validation foreach property of yours and exclude decorations you have already written!

You can call the method:

ModelState.Clear()

when MustBeDeleted is true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM