【问题标题】:Writing a CompareTo DataAnnotation Attribute编写 CompareTo DataAnnotation 属性
【发布时间】:2009-10-22 14:58:02
【问题描述】:

我想与字段进行比较(例如,确保开始时间在结束时间之前)。我正在使用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

有人对此有解决方案吗?或者验证一个字段与另一个字段值的不同方法?

【问题讨论】:

  • 您是如何执行验证的?数据注释只是属性,因此在分析特定方法是否有效时,这一点很重要。请发布验证本身的短代码 sn-p。
  • [CompareTo(CompareToOperation.GreaterThan, () =&gt; this.Start)] 不起作用,因为类在编译时而不是在运行时应用属性。这就是为什么您只能提供常量表达式的原因。 (并且() =&gt; this.Start 不是常量表达式。)

标签: c# validation data-annotations


【解决方案1】:

查看MVC2默认项目中的AccountMOdel,有一个属性PropertiesMustMatchAttribute应用于ChangePasswordModel,用于验证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);
    }
}

【讨论】:

    【解决方案2】:

    一种不太灵活但非常丑陋的方法是将其放在类上并使用反射。我没有测试过这个,所以我不确定它是否有效,但它确实可以编译:)

    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; }
    }
    

    【讨论】:

    • 您将如何在视图页面中显示来自此消息的验证错误?这是我遇到的障碍。
    • @Erx_VB.NExT.Coder:只需添加&lt;%= Html.ValidationMessage(string.Empty) %&gt;,您将收到此特定全局类验证错误的消息。
    • @Erx_VB.NExT.Coder :通过从 ValidationAttribute 子类创建类级别属性时,如果验证失败,则 ModelState => 中没有相应的键,它将是一个空字符串,但可以解决在下面提供的链接中提供,这将帮助您仅使用 html.ValidationMessage("urpropertyname") 在您的视图中显示错误消息。 stackoverflow.com/questions/4266632/…
    【解决方案3】:

    从表面上看,这是做不到的。

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

    我认为这个问题不是一个抽象的问题,而且您确实有一个真正的问题需要存在这样的验证器。可能是重复密码文本框? :-)

    在任何情况下,要解决您遇到的问题,您需要依赖于您工作的上下文。ASP.NET Web 窗体使用 ControlToCompare 做到了这一点,因为一切都是一个控件,而且我们有适当的命名容器根据一个简单的字符串很容易弄清楚。

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

    总而言之,事情(不)发生总是有原因的,在我看来,微软没有首先实施这种验证器的原因是 - 如果没有上述情况,这是不可能的。

    但是!我真的希望我是错的。我确实需要易于使用的比较验证...

    【讨论】:

      【解决方案4】:

      我认为你需要这样的东西:

      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();
       }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-08-31
        • 1970-01-01
        • 2014-03-22
        • 1970-01-01
        • 1970-01-01
        • 2014-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多