在Asp.Net MVC中可以用继承ValidationAttribute的方式,自定制实现Model两个中两个属性值的比较验证
具体应用场景为:要对两个属性值的大小进行验证
代码如下所示:
/// <summary> /// Specifies that the field must compare favourably with the named field, if objects to check are not of the same type /// false will be return /// </summary> public class CompareValuesAttribute : ValidationAttribute { /// <summary> /// The other property to compare to /// </summary> public string OtherProperty { get; set; } public CompareValues Criteria { get; set; } /// <summary> /// Creates the attribute /// </summary> /// <param name="otherProperty">The other property to compare to</param> public CompareValuesAttribute(string otherProperty, CompareValues criteria) { if (otherProperty == null) throw new ArgumentNullException("otherProperty"); OtherProperty = otherProperty; Criteria = criteria; } /// <summary> /// Determines whether the specified value of the object is valid. For this to be the case, the objects must be of the same type /// and satisfy the comparison criteria. Null values will return false in all cases except when both /// objects are null. The objects will need to implement IComparable for the GreaterThan,LessThan,GreatThanOrEqualTo and LessThanOrEqualTo instances /// </summary> /// <param name="value">The value of the object to validate</param> /// <param name="validationContext">The validation context</param> /// <returns>A validation result if the object is invalid, null if the object is valid</returns> protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // the the other property var property = validationContext.ObjectType.GetProperty(OtherProperty); // check it is not null if (property == null) return new ValidationResult(String.Format("Unknown property: {0}.", OtherProperty)); // check types var memberName = validationContext.ObjectType.GetProperties().Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName)).Select(p => p.Name).FirstOrDefault(); if (memberName == null) { memberName = validationContext.DisplayName; } if (validationContext.ObjectType.GetProperty(memberName).PropertyType != property.PropertyType) return new ValidationResult(String.Format("The types of {0} and {1} must be the same.", memberName, OtherProperty)); // get the other value var other = property.GetValue(validationContext.ObjectInstance, null); // equals to comparison, if (Criteria == CompareValues.EqualTo) { if (Object.Equals(value, other)) return null; } else if (Criteria == CompareValues.NotEqualTo) { if (!Object.Equals(value, other)) return null; } else { // check that both objects are IComparables if (!(value is IComparable) || !(other is IComparable)) return new ValidationResult(String.Format("{0} and {1} must both implement IComparable", validationContext.DisplayName, OtherProperty)); // compare the objects var result = Comparer.Default.Compare(value, other); switch (Criteria) { case CompareValues.GreaterThan: if (result > 0) return null; break; case CompareValues.LessThan: if (result < 0) return null; break; case CompareValues.GreatThanOrEqualTo: if (result >= 0) return null; break; case CompareValues.LessThanOrEqualTo: if (result <= 0) return null; break; } } // got this far must mean the items don't meet the comparison criteria return new ValidationResult(ErrorMessage); } } /// <summary> /// Indicates a comparison criteria used by the CompareValues attribute /// </summary> public enum CompareValues { EqualTo, NotEqualTo, GreaterThan, LessThan, GreatThanOrEqualTo, LessThanOrEqualTo }
应用的时候直接在指定的属性上添加此CompareValuesAttribute标签即可
【注:第一个参数是要与之比较的属性名,第二个参数表示两个属性值之间的大小关系,第三个参数表示错误提示信息】
public class EricSunModel { [Display(Name = "Ready Time")] public string ReadyTime { get; set; } [CompareValues("ReadyTime", CompareValues.GreaterThan, ErrorMessage = "Close time must be later than ready time")] [Display(Name = "Close Time")] public string CloseTime { get; set; } }
更多细节可以参考如下链接:
http://cncrrnt.com/blog/index.php/2011/01/custom-validationattribute-for-comparing-properties/
这里提供一个我自己的增强版本,可以添加另外一个依赖条件。
代码如下:
public class CompareValuesAttribute : ValidationAttribute { /// <summary> /// The other property to compare to /// </summary> public string OtherProperty { get; set; } public CompareCriteria Criteria { get; set; } /// <summary> /// The other dependent rule /// </summary> public string RulePropertyName { get; set; } public CompareCriteria RuleCriteria { get; set; } public object RulePropertyValue { get; set; } public bool CustomErrorMessage { get; set; } /// <summary> /// Compare values with other property based on other rules or not /// </summary> /// <param name="otherProperty">other property.</param> /// <param name="criteria">criteria</param> /// <param name="rulePropertyName">rule property name. (if don't based on other rules, please input null)</param> /// <param name="ruleCriteria">rule criteria. (if don't based on other rules, please input null)</param> /// <param name="rulePropertyValue">rule property value. (if don't based on other rules, please input null)</param> /// <param name="customErrorMessage">custom error message. (if don't need customize error message format, please input null)</param> public CompareValuesAttribute(string otherProperty, CompareCriteria criteria, string rulePropertyName, CompareCriteria ruleCriteria, object rulePropertyValue, bool customErrorMessage) { OtherProperty = otherProperty; Criteria = criteria; RulePropertyName = rulePropertyName; RuleCriteria = ruleCriteria; RulePropertyValue = rulePropertyValue; CustomErrorMessage = customErrorMessage; } /// <summary> /// Compare values with other property /// </summary> /// <param name="otherProperty"></param> /// <param name="criteria"></param> /// <param name="customErrorMessage"></param> public CompareValuesAttribute(string otherProperty, CompareCriteria criteria, bool customErrorMessage) { OtherProperty = otherProperty; Criteria = criteria; RulePropertyName = null; CustomErrorMessage = customErrorMessage; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (ValidateDependentRule(validationContext) == null) //validate dependent rule successful { if (OtherProperty == null) { return new ValidationResult(String.Format("Orther property is null.")); } // the the other property var property = validationContext.ObjectType.GetProperty(OtherProperty); // check it is not null if (property == null) { return new ValidationResult(String.Format("Unknown property: {0}.", OtherProperty)); } // check types var memberName = validationContext.ObjectType.GetProperties().Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName)).Select(p => p.Name).FirstOrDefault(); if (memberName == null) { memberName = validationContext.DisplayName; } if (validationContext.ObjectType.GetProperty(memberName).PropertyType != property.PropertyType) { return new ValidationResult(String.Format("The types of {0} and {1} must be the same.", memberName, OtherProperty)); } // get the other value var other = property.GetValue(validationContext.ObjectInstance, null); if (CompareValues(value, other, Criteria, validationContext) == null) { return null; } else { // got this far must mean the items don't meet the comparison criteria if (CustomErrorMessage) { return new ValidationResult(string.Format(ErrorMessage, other)); } else { return new ValidationResult(ErrorMessage); } } } else { return null; //dependent rule isn't exist or validate dependent rule failed } } private ValidationResult ValidateDependentRule(ValidationContext validationContext) { ValidationResult validateRuleResult = null; // has dependent rule if (RulePropertyName != null) { var rulePropertyName = validationContext.ObjectType.GetProperty(RulePropertyName); if (rulePropertyName == null) return new ValidationResult(String.Format("Unknown rule property name: {0}.", RulePropertyName)); var rulePropertyRealValue = rulePropertyName.GetValue(validationContext.ObjectInstance, null); validateRuleResult = CompareValues(rulePropertyRealValue, RulePropertyValue, RuleCriteria, validationContext); } return validateRuleResult; } private ValidationResult CompareValues(object targetValue, object otherValue, CompareCriteria compareCriteria, ValidationContext validationContext) { ValidationResult compareResult = new ValidationResult("Compare Values Failed."); // equals to comparison, if (compareCriteria == CompareCriteria.EqualTo) { if (Object.Equals(targetValue, otherValue)) compareResult = null; } else if (compareCriteria == CompareCriteria.NotEqualTo) { if (!Object.Equals(targetValue, otherValue)) compareResult = null; } else { // check that both objects are IComparables if (!(targetValue is IComparable) || !(otherValue is IComparable)) compareResult = new ValidationResult(String.Format("{0} and {1} must both implement IComparable", validationContext.DisplayName, OtherProperty)); // compare the objects var result = Comparer.Default.Compare(targetValue, otherValue); switch (compareCriteria) { case CompareCriteria.GreaterThan: if (result > 0) compareResult = null; break; case CompareCriteria.LessThan: if (result < 0) compareResult = null; break; case CompareCriteria.GreatThanOrEqualTo: if (result >= 0) compareResult = null; break; case CompareCriteria.LessThanOrEqualTo: if (result <= 0) compareResult = null; break; } } return compareResult; } } public enum CompareCriteria { EqualTo, NotEqualTo, GreaterThan, LessThan, GreatThanOrEqualTo, LessThanOrEqualTo }