Написание атрибута compareTo DataAnnotation
-
05-07-2019 - |
Вопрос
У меня есть ситуация, когда я хочу сравнить с полями (например, убедившись, что время начала задано раньше времени окончания).Я использую System.ComponentModel.DataAnnotations
атрибуты для моей проверки.
Моя первая мысль была примерно такой:
public enum CompareToOperation
{
EqualTo,
LessThan,
GreaterThan
}
public class CompareToAttribute : ValidationAttribute
{
CompareToOperation _Operation;
IComparable _Comparision;
public CompareToAttribute(CompareToOperation operation, Func<IComparable> 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
У кого-нибудь есть решение для этого?Или другой подход к проверке одного поля по сравнению со значением другого?
Решение
Очень уродливый способ, который не так гибок, это поместить его в класс и использовать рефлексию. Я не проверял это, поэтому я не уверен, что это работает, но он компилируется:)
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; }
}
Другие советы
Установите флажок AccountMOdel в проекте MVC2 по умолчанию. Для ChangePasswordModel применяется атрибут PropertiesMustMatchAttribute, чтобы проверить, совпадают ли NewPassword и 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 применяется к свойству и как таковой ограничен только этим свойством.
Я предполагаю, что вопрос не является абстрактным, и у вас действительно есть реальная проблема, которая требует наличия такого средства проверки.Возможно, это текстовое поле для повторения пароля?:-)
В любом случае, чтобы обойти возникшую у вас проблему, вам нужно полагаться на контекст, в котором вы работаете.ASP.NET Веб-формы сделали это с помощью ControlToCompare, и поскольку все является элементом управления, и у нас есть контейнеры для именования, довольно легко разобраться на основе простой строки.
В ASP.NET MVC вы теоретически можете сделать то же самое, НО!Клиентская сторона будет довольно простой и естественной - просто используйте #propertyName и делайте свои вещи на javascript.Однако на стороне сервера вам нужно было бы получить доступ к чему-то внешнему по отношению к вашему классу атрибутов - объекту запроса - и, насколько я понимаю, это "нет-нет".
В общем, всегда есть причина, по которой что-то (не) происходит, и, на мой взгляд, причина, по которой Microsoft в первую очередь не внедрила этот тип валидатора, заключается в том, что это невозможно без вещей, описанных выше.
НО!Я действительно надеюсь, что я ошибаюсь.Мне действительно нужно, чтобы проверка сравнения была простой в использовании...
Я думаю, тебе нужно что-то вроде этого:
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();
}
}