2010-02-17 93 views
41

從現在起我已經使用優秀的FluentValidation 庫來驗證我的模型類。在web應用程序中,我使用它與jquery.validate插件一起執行客戶端驗證。 一個缺點是,許多驗證邏輯在客戶端重複,不再集中在一個地方。使用數據註釋自定義模型驗證依賴屬性

因此,我正在尋找替代品。有many示例輸出there顯示使用數據註釋來執行模型驗證。它看起來很有希望。 我找不到的一件事是如何驗證依賴於另一個屬性值的屬性。

讓我們舉個例子以下模型:

public class Event 
{ 
    [Required] 
    public DateTime? StartDate { get; set; } 
    [Required] 
    public DateTime? EndDate { get; set; } 
} 

我想確保EndDateStartDate更大。我可以編寫一個自定義 驗證屬性,以延伸ValidationAttribute以執行自定義驗證邏輯。不幸的是我無法找到一個方法來獲得 模型實例:

public class CustomValidationAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     // value represents the property value on which this attribute is applied 
     // but how to obtain the object instance to which this property belongs? 
     return true; 
    } 
} 

我發現CustomValidationAttribute似乎做的工作,因爲它包含正在驗證的對象實例此ValidationContext屬性。不幸的是,該屬性僅在.NET 4.0中添加。所以我的問題是:我可以在.NET 3.5 SP1中實現相同的功能嗎?


UPDATE:

似乎FluentValidation already supports客戶端驗證和元數據在ASP.NET MVC 2

不過這將是很好的瞭解不過,如果數據標註可用於驗證有關的性質。

+0

有沒有人想過在相同的類/模型下一起獲取dataannotations和FluentValidation(驗證)的方法?如果這樣會很棒,我有一個關於這個與FV作者Jeremy討論的話題,你可以在這裏查看:http://fluentvalidation.codeplex.com/Thread/View.aspx?ThreadId=212371 – 2010-05-14 14:27:21

回答

28

MVC2附帶一個示例「PropertiesMustMatchAttribute」,它顯示瞭如何讓DataAnnotations爲您工作,它應該可以在.NET 3.5和.NET 4.0中工作。該樣本代碼如下所示:

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

當您使用屬性,而不是把它放在你的模型類的屬性,你把它的類本身:

[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")] 
public class ChangePasswordModel 
{ 
    public string NewPassword { get; set; } 
    public string ConfirmPassword { get; set; } 
} 

當「的IsValid 「調用你的自定義屬性,整個模型實例被傳遞給它,所以你可以通過這種方式獲得相關的屬性值。您可以輕鬆地遵循此模式來創建日期比較屬性,甚至可以創建更一般的比較屬性。

Brad Wilson has a good example on his blog顯示瞭如何添加驗證的客戶端部分,但我不確定該示例是否可以在.NET 3.5和.NET 4.0中工作。

+2

我試過這個,但我永遠不會得到驗證錯誤,以顯示在我的aspx頁面/視圖。我已經嘗試使用空字符串調用validationmessage,也嘗試使用驗證摘要,並沒有在那裏顯示(就像它在properties的例子中一樣) – 2010-05-14 14:21:02

+1

我浪費了好幾個小時試圖讓這個工作,並認爲我的代碼是錯誤的,直到我終於意識到我只是沒有正確地測試它,當我看到這篇文章:http://stackoverflow.com/questions/3586324/custom-validation-attribute-is-not-called-asp-net-mvc(基本上字段/屬性級別驗證首先觸發,所以您需要在啓動類級屬性isvalid()方法之前完全驗證這些驗證。 – jimasp 2011-11-08 14:42:43

+0

我後來在字段驗證問題後發現了一個更好的類驗證帖子:http://stackoverflow.com/questions/3099397/property-level-validation-errors-hinder-the-validation-of-class-level-validation – jimasp 2011-11-08 15:05:49

3

由於.NET 3.5的DataAnnotations方法不允許您提供經過驗證的實際對象或驗證上下文,因此您將不得不做一些技巧來完成此操作。我必須承認我對ASP.NET MVC並不熟悉,所以我不能說如何與MCV一起完成此任務,但是您可以嘗試使用線程靜態值來傳遞參數本身。這是一個可能有效的例子。

首先建立某種形式的「對象範圍」,使您可以通過周圍對象,而無需通過調用堆棧傳遞他們:

public sealed class ContextScope : IDisposable 
{ 
    [ThreadStatic] 
    private static object currentContext; 

    public ContextScope(object context) 
    { 
     currentContext = context; 
    } 

    public static object CurrentContext 
    { 
     get { return context; } 
    } 

    public void Dispose() 
    { 
     currentContext = null; 
    } 
} 

接下來,建立驗證程序使用ContextScope:

public class CustomValidationAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     Event e = (Event)ObjectContext.CurrentContext; 

     // validate event here. 
    } 
} 

最後但並非最不重要的,確保對象是過去通過ContextScope各地:

Event eventToValidate = [....]; 
using (var scope new ContextScope(eventToValidate)) 
{ 
    DataAnnotations.Validator.Validate(eventToValidate); 
} 

這非常有用?

+0

Steven,這看起來不錯。我唯一要修改的是將當前上下文存儲到HttpContext中,而不是使用'ThreadStatic'。我只是簡單地在ASP.NET應用程序中避免它。 – 2010-02-17 20:03:46

+0

你能解釋爲什麼你相信我們應該在ASP.NET應用程序中避免這種情況嗎?我在我自己的生產應用程序中使用這個構造,所以我非常感興趣爲什麼這是不好的。 – Steven 2010-02-17 22:06:52

+4

互聯網上有很多文章爲什麼這是不好的。下面是一個:http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx ThreadStatic的問題是,在ASP.NET中,您無法控制線程的生命週期,並且由於線程被重用,有些情況下變量可能會得到修改。如果你使用異步頁面和控制器,事情會變得更加醜陋。例如,請求可能在一個線程上開始,並在另一個線程上完成。所以在ASP.NET中,只有**方法才能擁有一個真正的每個請求存儲是HttpContext。 – 2010-02-17 22:37:38

14

我有這個非常的問題,最近開源了我的解決方案: http://foolproof.codeplex.com/

萬無一失的解決方案,以上面的例子是:

public class Event 
{ 
    [Required] 
    public DateTime? StartDate { get; set; } 

    [Required] 
    [GreaterThan("StartDate")] 
    public DateTime? EndDate { get; set; } 
} 
+0

我認爲GreaterThan日期驗證只適用於美國格式日期 – GraemeMiller 2012-06-29 20:00:29

7

相反,PropertiesMustMatch的CompareAttribute的,可以在MVC3使用。根據這個鏈接http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1

public class RegisterModel 
{ 
    // skipped 

    [Required] 
    [ValidatePasswordLength] 
    [DataType(DataType.Password)] 
    [Display(Name = "Password")] 
    public string Password { get; set; }      

    [DataType(DataType.Password)] 
    [Display(Name = "Confirm password")] 
    [Compare("Password", ErrorMessage = "The password and confirmation do not match.")] 
    public string ConfirmPassword { get; set; } 
} 

CompareAttribute是一個新的,非常有用的驗證,是不是的 System.ComponentModel.DataAnnotations實際上 部分, 但已被添加到 的System.Web由團隊製作的.Mvc DLL。雖然 不是特別好命名(唯一 比較它使是檢查 平等,所以也許E​​qualTo會 更明顯),很容易從 看到使用這個驗證檢查 一個屬性的值等於 另一個屬性的值。您可以從代碼 中看到,屬性 需要一個字符串屬性,它是 與 比較的其他屬性的名稱。這種類型的驗證器的經典用法 是我們 正在使用它在這裏:密碼 確認。

3

過了一小會兒,因爲你的問題被問,但如果你還是喜歡的元數據(至少暫時),下面還有另一種替代解決方案,它可以讓你的屬性提供不同的邏輯表達式:

[Required] 
public DateTime? StartDate { get; set; }  
[Required] 
[AssertThat("StartDate != null && EndDate > StartDate")] 
public DateTime? EndDate { get; set; } 

它適用於服務器以及客戶端。更多詳情can be found here

+0

非常感謝你這個庫對大多數事情都非常有用。 – Enzero 2014-11-21 21:29:20