0

I'm new to MVC and am working through some validations. I've done my basic field validations at the model level (required, ranges, etc). I'm now working on a page which is essentially building a scorecard with weights. I need each of the weights for each criteria to add up to 1.

I'm not sure if I can validate this in the model because I need the ability to create each of these objects in the database as they're added. How would one go about validating that each of these properties add up to 1 before the user moves on to the next step?

sdre9
  • 1
  • 3
    You can create custom validation. – Carlo Luther Dec 22 '16 at 14:53
  • My first thought is to use an IValidatableObject interface on your ViewModel, which gives you a way to write custom code which creates validation errors just like the Attribute validation code does. http://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject – Graham Dec 22 '16 at 16:00

1 Answers1

0

Basically for the backend you need to create a custom validation that can be used as a attribute over the field to validate.

This is an example of a validation that checks if "number is greater than another filed".

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class NumberGreaterThanAttribute : ValidationAttribute, IClientValidatable
    {
        string otherPropertyName;


        public NumberGreaterThanAttribute(string otherPropertyName, string errorMessage)
            : base(errorMessage)
        {
            this.otherPropertyName = otherPropertyName;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            ValidationResult validationResult = ValidationResult.Success;


            if (value == null)
                return validationResult;

            try
            {
                // Using reflection we can get a reference to the other date property, in this example the project start date
                var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
                object referenceProperty = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);

                if (referenceProperty == null)
                    return validationResult;

                double referenceProperty_value = System.Convert.ToDouble(referenceProperty, null);
                double currentField_value = System.Convert.ToDouble(value, null);


                if (currentField_value <= referenceProperty_value)
                {
                    validationResult = new ValidationResult(ErrorMessageString);
                }

            }
            catch (Exception ex)
            {

                throw ex;
            }


            return validationResult;
        }


        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            //string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
            string errorMessage = ErrorMessageString;

            // The value we set here are needed by the jQuery adapter
            ModelClientValidationRule numberGreaterThanRule = new ModelClientValidationRule();
            numberGreaterThanRule.ErrorMessage = errorMessage;
            numberGreaterThanRule.ValidationType = "numbergreaterthan"; // This is the name the jQuery adapter will use
            //"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
            numberGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);

            yield return numberGreaterThanRule;
        }
    }

REMEMBER: frontend validation is not automatically enabled, for custom/newly created validations. If you want to create the same validation rules for the frontend you'll need to create a new frontend custom validation. I generally use validate.js library for the frontend. Still, frontend validation is not strictly required (but I'd suggest to use it in any case) if you don't mind pushing data back and forth from server to client, especially if data is small.

The ViewModel/Model will be received and parsed by modelstate when the backend receives the request(generally a POST). The model/viewmodel should be decorated on the fields that require the validation as so:

    public Int64? AtomicQtyMultiplePurchaseMin { get; set; }


    [NumberGreaterThanAttribute("AtomicQtyMultiplePurchaseMin", "Must be greater than min num. qty. of purchase")]
    public Int64? AtomicQtyMultiplePurchaseMax { get; set; }

I generally place the custom validation classes in the following folder (but you can place it where you want in the project):

enter image description here

Carlo Luther
  • 2,402
  • 7
  • 46
  • 75
  • This may be a dumb question, but where would you add the custom validation? In the controller? – sdre9 Dec 22 '16 at 15:43
  • Where you want. You can create a folder in the root project called "mycustomvalidations". When you apply the validation attribute to the viewmodel's filed, just remember to reference the validation class with the using statement. – Carlo Luther Dec 22 '16 at 15:45