2009-10-22 77 views
9

我有一種情況,我想比較字段(例如,確保開始時間在結束時間之前)。我正在使用System.ComponentModel.DataAnnotations屬性進行驗證。寫一個CompareTo DataAnnotation屬性

我首先想到的是這樣的:

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 

有沒有人有一個解決方案?或者與另一個字段的值相比驗證一個字段的方法不同?

+0

您如何執行驗證?數據註釋只是屬性,所以在分析某個特定方法是否可行時,這很重要。請發佈驗證本身的簡短代碼片段。 – Aaronaught

+0

'[CompareTo(CompareToOperation.GreaterThan,()=> this.Start)]'不起作用,因爲類在編譯時獲取屬性,而不是在運行時應用。這就是爲什麼你只能提供常量表達式的原因。 (和'()=> this.Start'不是一個常量表達式。) – Regent

回答

8

一個非常醜陋的方式,這不是幾乎一樣靈活的方式是把它放在類和使用反射。我沒有測試過這一點,所以我沒有真正確定它的工作原理,但它編譯:)

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

以及如何在您的視圖頁面中顯示來自此消息的驗證錯誤?這是我遇到的一個路障。 –

+1

@ Erx_VB.NExT.Coder:只需添加'<%= Html.ValidationMessage(string.Empty)%>',您將得到此特定* global *類驗證錯誤的消息。 –

+0

@ Erx_VB.NExT.Coder:當通過來自ValidationAttribute的子類創建類級屬性時,如果驗證失敗,那麼ModelState =>中將沒有對應的鍵,它將是一個空字符串,但是在下面提供的鏈接中提供瞭解決方法將通過使用html.ValidationMessage(「urpropertyname」)幫助U在你的View中顯示錯誤消息。 http://stackoverflow.com/questions/4266632/unable-to-set-membernames-from-custom-validation-attribute-in-mvc2 – Vipresh

0

從它的外觀來看,這是無法完成的。

ValidationAttribute應用於屬性,因此僅限於該屬性。

我認爲這個問題不是一個抽象的問題,而且您確實有一個真正的問題需要驗證器的存在。可能是重複的密碼文本框? :-)

在任何情況下,要解決您的問題,您需要依賴您工作的上下文。ASP.NET Web Forms使用ControlToCompare來完成此任務,並且由於所有內容都是控件,因此我們命名容器就位而言,根據簡單的字符串來判斷事情是相當容易的。

在ASP.NET MVC中,你可以在理論上做同樣的事情,但!客戶端將是相當簡單和自然 - 只需使用#PropertyName並在JavaScript中做你的東西。 Serverside雖然你需要訪問你的屬性類外部的東西 - Request對象 - 這是一個不錯的,就我而言。

總而言之,事情總是有原因的(沒有)發生,並且在我看來,微軟沒有在第一個地方實施這種驗證器的原因是 - 沒有上述事情是不可能的。

但是!我真的希望我錯了。我確實需要比較驗證,易於使用...

0

我想你需要的東西是這樣的:

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

檢查的AccountMOdel在MVC2的默認項目,有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); 
    } 
} 
相關問題