0

I have the following poco:

public class CabinetItem
{
    [Required]
    [Display(...)]
    public double Width { get; set; }

    public double MinWidth { get; }
}

What I am trying to figure out is how do I validate that Width is greater than MinWidth when MinWidth could be anything? The minimum width is a constraint that is dependent on the cabinet item. Note: there is also a MaxWidth that I left out to simplify this question.

Jordan
  • 9,642
  • 10
  • 71
  • 141

2 Answers2

1

Option 1:

foolproof nuget package might be very useful in your case.

Install foolproof nuget package and use its extra useful attributes like the following:

public class CabinetItem
{
    [Required]
    [Display(...)]
    [GreaterThan("MinWidth")]
    public double Width { get; set; }

    public double MinWidth { get; }
}

There are other features as well:

  • [Is]
  • [EqualTo]
  • [NotEqualTo]
  • [GreaterThan]
  • [LessThan]
  • [GreaterThanOrEqualTo]
  • [LessThanOrEqualTo]

Resources: Is there a way through data annotations to verify that one date property is greater than or equal to another date property?

Community
  • 1
  • 1
Tushar Gupta
  • 15,504
  • 1
  • 29
  • 47
  • The custom validation attribute you are linking to is Silverlight only. I believe the analog for MVC would be ValidationAttribute. Correct me if that is not so. – Jordan Feb 23 '15 at 16:54
  • I have seen this, but doesn't this require a post back to the server to validate? Is this what unobtrusive validation does behind the scenes? – Jordan Feb 23 '15 at 16:54
  • yes you are correct for the first one i'll remove it but for the second the unobtrusive ajax can be of any get/ post method. please have a look at http://www.asp.net/mvc/overview/older-versions/creating-a-mvc-3-application-with-razor-and-unobtrusive-javascript – Tushar Gupta Feb 23 '15 at 16:59
0

You can create a CustomValidationAttribute

Take this as an example :

    public class GreaterThanAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly string _testedPropertyName;
        private readonly bool _allowEqualValues;
        private readonly string _testedPropertyDisplayName;

        public override string FormatErrorMessage(string displayName)
        {
            return string.Format(ErrorMessages.GreaterThan_Message, displayName, _testedPropertyDisplayName);
        }

        public GreaterThanAttribute(string testedPropertyName, Type resourceType, string testedPropertyDisplayNameKey, bool allowEqualValues = false)
        {
            _testedPropertyName = testedPropertyName;
            _allowEqualValues = allowEqualValues;
            var rm = new ResourceManager(resourceType);
            _testedPropertyDisplayName = rm.GetString(testedPropertyDisplayNameKey);            
        }

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

            if (propertyTestedInfo == null)
            {
                return new ValidationResult(string.Format("unknown property {0}", _testedPropertyName));
            }

            var propertyTestedValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null);

            if (value == null || !(value is Decimal))
            {
                return ValidationResult.Success;
            }

            if (propertyTestedValue == null || !(propertyTestedValue is Decimal))
            {
                return ValidationResult.Success;
            }

            // Compare values
            if ((Decimal)value >= (Decimal)propertyTestedValue)
            {
                if (_allowEqualValues && value == propertyTestedValue)
                {
                    return ValidationResult.Success;
                }
                else if ((Decimal)value > (Decimal)propertyTestedValue)
                {
                    return ValidationResult.Success;
                }
            }

            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.DisplayName),
                ValidationType = "greaterthan"
            };
            rule.ValidationParameters["propertytested"] = _testedPropertyName;
            rule.ValidationParameters["allowequalvalues"] = _allowEqualValues;
            yield return rule;
        }
    }

You can use it like this :

public Decimal MyProperty1 { get; set; }

[GreaterThanAttribute("MyProperty1", typeof(Strings), "Error_String")]
public Decimal MyProperty2 { get; set; }

[GreaterThanAttribute("MyProperty2", typeof(Strings), "Error_String")]
public Decimal MyProperty3 { get; set; }

And in client side you can add this for client validation:

jQuery.validator.unobtrusive.adapters.add('greaterthan', ['propertytested', 'allowequalvalues'], function (options) {
            options.params["allowequalvalues"] = options.params["allowequalvalues"] === "True" ||
                                                 options.params["allowequalvalues"] === "true" ||
                                                 options.params["allowequalvalues"] === true ? true : false;

            options.rules['greaterthan'] = options.params;
            options.messages['greaterthan'] = options.message;
        });
jQuery.validator.addMethod("greaterthan", function (value, element, params) {        
            var properyTestedvalue= $('input[name="' + params.propertytested + '"]').val();
            if (!value || !properyTestedvalue) return true;
            return (params.allowequalvalues) ? parseFloat(properyTestedvalue) <= parseFloat(value) : parseFloat(properyTestedvalue) < parseFloat(value);
        }, ''); 
sabotero
  • 4,265
  • 3
  • 28
  • 43
  • This might work. But I'm not emitting maximum width directly to the client. I am as a hidden value. – Jordan Feb 23 '15 at 18:28
  • well that it's not really a problem as `$('input[name="' + params.propertytested + '"]')` will obtaint even a hidden field – sabotero Feb 23 '15 at 21:26
  • you have to use `@Html.HiddenFor(m=>m.MinWith)` for render the hidden field – sabotero Feb 23 '15 at 21:30