Custom MVC validation attribute

Finding my self in a new-old situation i needed to create a custom validation attribute.

To create an custom validation attribute we inherit from the abstract class “ValidationAttribute” in System.ComponentModel.DataAnnotations. This allows for server side validation by invoking the method “IsValid”.

I decided to implement an “MoreThan” attribute which checks that the one value in my model is more than another. For this we need the validator to compare the model property with another property.

This is how the validator class looks like.

public class MoreThanAttribute : ValidationAttribute
{
    private readonly string _comparisonProperty;

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

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

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

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

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

        if (currentValue <= comparisonValue) {

            ErrorMessage = string.Format("{0} must be more than {1}.", 
                validationContext.MemberName,
                _comparisonProperty.ToLower());

            return new ValidationResult(
                ErrorMessage, 
                new string[] { validationContext.MemberName });
        }

        return ValidationResult.Success;
    }

}

To use it we simply add the “MoreThan” attribute to our model property with a comparison property of a string.

public class MyModel
{
    public int Start { get; set; }

    [MoreThan("Start", ErrorMessage = "Stop must be more than start.")]
    public int Stop { get; set; }
}

If we want to use the jQuery obtrusive validation we need to add an “IClientModelValidator” interface to our custom validation class. This makes the internal tag helper to know about the validation.

public class MoreThanAttribute : 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);
    }

}

So how can we test if the custom validator work? Lets implement a simple xunit test where we test the validitiy and the error message we want it to show.

public void Stop_must_be_more_than_start()
{
    var model = new MyModel
    {
        Start = 10,
        Stop = 9
    };

    var ctx = new ValidationContext(model);
    var results = new List<ValidationResult>();
    var valid = Validator.TryValidateObject(model, ctx, results, true);

    Assert.False(valid);
    var failure = Assert.Single(results, x => x.ErrorMessage == "Stop must be more than start.");
    Assert.Single(failure.MemberNames, x => x == "Stop");
}

Now, go create your custom validators to satisfy your needs.

Leave a Comment

Your email address will not be published. Required fields are marked *