Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Validation Attributes: Comparing two properties in the same model

Is there a way to create a custom attribute in ASP.NET Core to validate if one date property is less than other date property in a model using ValidationAttribute.

Lets say I have this:

public class MyViewModel 
{
    [Required]
    [CompareDates]
    public DateTime StartDate { get; set; }

    [Required]
    public DateTime EndDate { get; set; } = DateTime.Parse("3000-01-01");
}

I am trying to use something like this:

    public class CompareDates : ValidationAttribute
{
    public CompareDates()
        : base("") { }

    public override bool IsValid(object value)
    {
        return base.IsValid(value);
    }

}

I found other SO post that proposes to use another library, But I prefer to stick with ValidationAttribute if that was doable.

like image 331
Hussein Salman Avatar asked Jan 27 '17 18:01

Hussein Salman


2 Answers

You can create a custom validation attribute for comparison two properties. It's a server side validation:

public class MyViewModel
{
    [DateLessThan("End", ErrorMessage = "Not valid")]
    public DateTime Begin { get; set; }

    public DateTime End { get; set; }
}

public class DateLessThanAttribute : ValidationAttribute
{
    private readonly string _comparisonProperty;

    public DateLessThanAttribute(string comparisonProperty)
    {
         _comparisonProperty = comparisonProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        ErrorMessage = ErrorMessageString;
        var currentValue = (DateTime)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);

        if (property == null)
            throw new ArgumentException("Property with this name not found");

        var comparisonValue = (DateTime)property.GetValue(validationContext.ObjectInstance);

        if (currentValue > comparisonValue)
            return new ValidationResult(ErrorMessage);

        return ValidationResult.Success;
    }
}

Update: If you need a client side validation for this attribute, you need implement an IClientModelValidator interface:

public class DateLessThanAttribute : ValidationAttribute, IClientModelValidator
{
    ...
    public void AddValidation(ClientModelValidationContext context)
    {
        var error = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
        context.Attributes.Add("data-val", "true");
        context.Attributes.Add("data-val-error", error);
    }
}

The AddValidation method will add attributes to your inputs from context.Attributes.

enter image description here

You can read more here IClientModelValidator

like image 109
Alexander Surkov Avatar answered Oct 18 '22 21:10

Alexander Surkov


As one possible option self-validation:

You just need to Implement an interface IValidatableObject with the method Validate(), where you can put your validation code.

public class MyViewModel : IValidatableObject
{
    [Required]
    public DateTime StartDate { get; set; }

    [Required]
    public DateTime EndDate { get; set; } = DateTime.Parse("3000-01-01");

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        int result = DateTime.Compare(StartDate , EndDate);
        if (result < 0)
        {
            yield return new ValidationResult("start date must be less than the end date!", new [] { "ConfirmEmail" });
        }
    }
}
like image 17
Curiousdev Avatar answered Oct 18 '22 20:10

Curiousdev