Custom Validation Attribute for String with Underneath Property Validation
Posted on: 2015-03-19
Validation can occur with an attribute during client side and server side. For example, one simple validation can be to ensure that a property string is all to lower case. This can be a simple attribute like the following one.
/// <summary> /// Validate that the string is in lower case /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class LowerCaseString : BaseDataAnnotation { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string model = value as string; if (model != null) { if(string.Equals(model, model.ToLowerInvariant(), StringComparison.InvariantCulture)) { return ValidationResult.Success; } } return base.ReturnDefaultErrorMessage(string.Format(Resources.DataAnnotationErrorMessage.StringNotLowerCase, validationContext.DisplayName) , new[] { validationContext.MemberName }); } }
public class StockOrder { [Required] [LowerCaseString] [Display(ResourceType = typeof(ModelPropertyDisplayName), Name = "StockSymbol")] public Symbol StockSymbol { get; set; } }
You may think that you should set the LowerCaseString to the string property in the beneath property's class. However, we cannot only set the attribute on the property class because the graphical user interface need to add the validation to the property and not the property of a property. To fix this problem, it is possible to add a property to the attribute with the goal of specifying the property to validate underneath.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class LowerCaseString : BaseDataAnnotation { public string InnerProperty { get; set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string model; if (value is string || String.IsNullOrEmpty(InnerProperty)) { model = value as string; } else { Type thisType = value.GetType(); PropertyInfo theMethod = thisType.GetProperty(InnerProperty); model = (string)theMethod.GetValue(value); } if (model != null) { if(string.Equals(model, model.ToLowerInvariant(), StringComparison.InvariantCulture)) { return ValidationResult.Success; } } return base.ReturnDefaultErrorMessage(string.Format(Resources.DataAnnotationErrorMessage.StringNotLowerCase, validationContext.DisplayName) , new[] { validationContext.MemberName }); } }
The InnerProperty property is checked to see if set and if it is set on an attribute that is not a string than by reflection we get the property value and validate. The goal of verifying if the property is a string is for advanced scenario where the validation is done on a class on the Model and on a string on the ViewModel. This situation can occur if you are working with a Model-ViewModel architecture and that you transfer automatically with AutoMapper Data Annotation validation.
public class StockOrder { [Required] [LowerCaseString(InnerProperty = "Value")] [Display(ResourceType = typeof(ModelPropertyDisplayName), Name = "StockSymbol")] public Symbol StockSymbol { get; set; } }
At the end, what is important is that you can specify inside an attribute which property to validate and having the option to be more specific in some scenario.