编写CompareTo DataAnnotation属性

我有一种情况,我想与字段进行比较(例如,确保开始时间在结束时间之前)。 我正在使用System.ComponentModel.DataAnnotations属性进行validation。

我的第一个想法是这样的:

 public enum CompareToOperation { EqualTo, LessThan, GreaterThan } public class CompareToAttribute : ValidationAttribute { CompareToOperation _Operation; IComparable _Comparision; public CompareToAttribute(CompareToOperation operation, Func comparison) { _Operation = operation; _Comparision = comparison(); } public override bool IsValid(object value) { if (!(value is IComparable)) return false; switch (_Operation) { case CompareToOperation.EqualTo: return _Comparision.Equals(value); case CompareToOperation.GreaterThan: return _Comparision.CompareTo(value) == 1; case CompareToOperation.LessThan: return _Comparision.CompareTo(value) == -1; } return false; } } public class SimpleClass { public DateTime Start {get;set;} [CompareTo(CompareToOperation.GreaterThan, () => this.Start)] // error here public DateTime End {get;set;} } 

但这不起作用,有一个编译器错误,其中标记了属性:

 Expression cannot contain anonymous methods or lambda expressions 

有人有解决方案吗? 或者一种不同的方法来validation一个字段与另一个字段的值相比?

一种非常难看的方式就是把它放在课堂上并使用reflection。 我没有测试过这个,所以我不确定它是否有效,但确实可以编译:)

 public enum CompareToOperation { EqualTo, LessThan, GreaterThan } public class CompareToAttribute : ValidationAttribute { CompareToOperation _Operation; string _ComparisionPropertyName1; string _ComparisionPropertyName2; public CompareToAttribute(CompareToOperation operation, string comparisonPropertyName1, string comparisonPropertyName2) { _Operation = operation; _ComparisionPropertyName1 = comparisonPropertyName1; _ComparisionPropertyName2 = comparisonPropertyName2; } private static IComparable GetComparablePropertyValue(object obj, string propertyName) { if (obj == null) return null; var type = obj.GetType(); var propertyInfo = type.GetProperty(propertyName); if (propertyInfo == null) return null; return propertyInfo.GetValue(obj, null) as IComparable; } public override bool IsValid(object value) { var comp1 = GetComparablePropertyValue(value, _ComparisionPropertyName1); var comp2 = GetComparablePropertyValue(value, _ComparisionPropertyName2); if (comp1 == null && comp2 == null) return true; if (comp1 == null || comp2 == null) return false; var result = comp1.CompareTo(comp2); switch (_Operation) { case CompareToOperation.LessThan: return result == -1; case CompareToOperation.EqualTo: return result == 0; case CompareToOperation.GreaterThan: return result == 1; default: return false; } } } [CompareTo(CompareToOperation.LessThan, "Start", "End")] public class SimpleClass { public DateTime Start { get; set; } public DateTime End { get; set; } } 

检查MVC2的默认项目中的AccountMOdel,有一个属性PropertiesMustMatchAttribute应用于ChangePasswordModel以validationNewPassword和ConfirmPassword匹配

  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public sealed class PropertiesMustMatchAttribute : ValidationAttribute { private const string _defaultErrorMessage = "'{0}' and '{1}' do not match."; private readonly object _typeId = new object(); public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty) : base(_defaultErrorMessage) { OriginalProperty = originalProperty; ConfirmProperty = confirmProperty; } public string ConfirmProperty { get; private set; } public string OriginalProperty { get; private set; } public override object TypeId { get { return _typeId; } } public override string FormatErrorMessage(string name) { return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, OriginalProperty, ConfirmProperty); } public override bool IsValid(object value) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value); object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value); object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value); return Object.Equals(originalValue, confirmValue); } } 

从它的外观来看,这是不可能做到的。

ValidationAttribute应用于属性,因此仅限于该属性。

我认为这个问题不是一个抽象问题,你确实有一个真正的问题,需要这样一个validation器。 可能是重复密码文本框? 🙂

在任何情况下,要解决这个问题,你需要依赖你所使用的上下文.ASP.NET Web Forms使用ControlToCompare来完成它,因为一切都是一个控件,我们有适当的命名容器,它很容易计算基于简单字符串的东西。

在ASP.NET MVC中你理论上可以做同样的事情,但是! 客户端将相当简单和自然 – 只需使用#PropertyName并在javascript中执行您的操作。 Serverside虽然您需要访问属性类外部的东西–Request对象 – 但就我而言,这是不可能的。

总而言之,总有一个理由(不)发生事情,在我看来,微软没有首先实现这种validation器的原因是 – 如果没有上述内容,这是不可能的。

但! 我真的希望我错了。 我需要比较validation才能使用…

我想你需要这样的东西:

 public class EqualsAttribute : ValidationAttribute { private readonly String _To; public EqualsAttribute(String to) { if (String.IsNullOrEmpty(to)) { throw new ArgumentNullException("to"); } if (String.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } _To = to; } protected override Boolean IsValid(Object value, ValidationContext validationContext, out ValidationResult validationResult) { validationResult = null; var isValid = IsValid(value, validationContext); if (!isValid) { validationResult = new ValidationResult( FormatErrorMessage(validationContext.DisplayName), new [] { validationContext.MemberName }); } return isValid; } private Boolean IsValid(Object value, ValidationContext validationContext) { var propertyInfo = validationContext.ObjectType.GetProperty(_To); if (propertyInfo == null) { return false; } var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null); return Equals(value, propertyValue); } public override Boolean IsValid(Object value) { throw new NotSupportedException(); } }