Journal: Software Development

Email: mail@appsoftware.com

Software development notes and articles

A journal for sharing all things software development related


Custom Validation Attribute (ValidationAttribute / Data Annotation)

Thu, 27 Jun 2019 11:45 UTC

This sample demonstrates how to write a custom ValidationAttribute for validating dependant properties on a model.

Put the attribute on the model class

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Code.Samples;

namespace Code.Samples.Common.ValidationAttributes
{
    public class CompareListValuePropertiesValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);

            ValidationResult validationResult = null;

            object instance = validationContext.ObjectInstance;

            object updateItemsObj = properties.Find("UpdateItems", true).GetValue(instance);

            var updateItems = (List<UpdateItem>) updateItemsObj;

            bool hasError = false;

            var errorMemberNames = new List<string>();

            if (updateItems != null && updateItems.Any())
            {
                foreach (var updateItem in updateItems)
                {
                    // Loop values

                    if (updateItem.Type == UpdateItemType.Net)
                    {
                        _ = decimal.TryParse(updateItem.Value, out var netValue);

                        if (netValue == 0m)
                        {
                            // If net value is 0, find corresponding item gross value

                            var grossUpdateItem = updateItems.FirstOrDefault(x => x.ItemCorrelationId == updateItem.ItemCorrelationId && x.Type == UpdateItemType.Gross);

                            if (grossUpdateItem != null)
                            {
                                _ = decimal.TryParse(grossUpdateItem.Value, out var grossValue);

                                if (grossValue != 0m)
                                {
                                    // If value is non 0, raise a validation error

                                    hasError = true;

                                    var listIndex = updateItems.FindIndex(x => x.ItemCorrelationId == updateItem.ItemCorrelationId && x.Type == UpdateItemType.Gross);

                                    // Add member names for validation result so that error element can be highlighted in UI

                                    errorMemberNames.Add($"updateItems[{listIndex}]");
                                }
                            }
                        }
                    }
                }

                if (hasError)
                {
                    validationResult = new ValidationResult("One or more items has a non zero gross value where the net value is zero", errorMemberNames);
                }
                else
                {
                    validationResult = ValidationResult.Success;
                }
            }
            else
            {
                validationResult = ValidationResult.Success;
            }

            return validationResult;
        }
    }
}

The information on this site is provided “AS IS” and without warranties of any kind either express or implied. To the fullest extent permissible pursuant to applicable laws, the author disclaims all warranties, express or implied, including, but not limited to, implied warranties of merchantability, non-infringement and suitability for a particular purpose.