2009-10-02 73 views
4

我有一個簡單的地址錄入應用程序,我試圖使用IDataErrorInfo接口作爲explained on the asp.net site在asp.net中使用IDataErrorInfo mvc

它對於可以單獨驗證的項目非常適用,但當某些項目依賴於其他項目時效果不佳。例如,驗證郵政編碼取決於國家:

private string _PostalCode; 
    public string PostalCode 
    { 
     get 
     { 
      return _PostalCode; 
     } 
     set 
     { 
      switch (_Country) 
      { 
       case Countries.USA: 
        if (!Regex.IsMatch(value, @"^[0-9]{5}$")) 
         _errors.Add("PostalCode", "Invalid Zip Code"); 
        break; 
       case Countries.Canada: 
        if (!Regex.IsMatch(value, @"^([a-z][0-9][a-z]) ?([0-9][a-z][0-9])$", RegexOptions.IgnoreCase)) 
         _errors.Add("PostalCode", "Invalid postal Code"); 
        break; 
       default: 
        throw new ArgumentException("Unknown Country"); 
      } 
      _PostalCode = value; 
     } 
    } 

因此國家已經設置後,你只能驗證郵政編碼,但似乎沒有控制順序的方式。

我可以使用來自IDataErrorInfo的錯誤字符串,但不會顯示在該字段旁邊的Html.ValidationMessage中。

回答

5

對於更復雜的業務規則驗證,而不是類型驗證,實現設計模式(如服務層)可能會更好。您可以檢查ModelState並根據您的邏輯添加錯誤。

您可以查看這裏

http://www.asp.net/learn/mvc/tutorial-29-cs.aspx

模式羅布Conroys例如這篇文章在數據註釋AY也有用。

http://www.asp.net/learn/mvc/tutorial-39-cs.aspx

希望這會有所幫助。

+0

關於此主題的asp.net教程(http://www.asp.net/learn/mvc/tutorial-38-cs.aspx)也許對您有幫助,可以幫助您瞭解如何開始。 – 2009-10-02 20:58:18

0

關於對字符串錯誤的評論,IDataErrorInfo的和Html.ValidationMessage,您可以使用顯示對象的水平與現場級的錯誤消息:

Html.ValidationMessage("address", "Error") 

Html.ValidationMessage("address.PostalCode", "Error") 

在你的控制器裝點POST方法處理參數的對象與[綁定(前綴=「地址」)]。在HTML中,將輸入字段命名爲...

<input id="address_PostalCode" name="address.PostalCode" ... /> 

我通常不使用Html助手。請注意id和name之間的命名約定。

1

以下是我找到的最佳解決方案,用於比簡單數據註釋模型更復雜的驗證。

我敢肯定,我並不孤單,試圖執行IDataErrorInfo並看到它只爲我創建了兩種實現方法。我在等待一分鐘 - 我是否必須在那裏進行編寫我自己的一切從頭開始的自定義例程?還有 - 如果我有模型級別的東西來驗證。當你決定使用它時,似乎你自己決定使用它,除非你想在你的IDataErrorInfo實現中使用like thisthis

我碰巧和提問者有完全相同的問題。我想驗證美國郵政,但只有當國家被選爲美國。顯然,模型級別的數據註釋不會有任何好處,因爲這不會導致郵編以紅色突出顯示爲錯誤。 [可以在PropertiesMustMatchAttribute類的MVC 2示例項目中找到類級別數據註釋的很好示例]。

的解決方案很簡單:

首先,你需要註冊在Global.asax的一個ModelBinder的。如果你願意,你可以將它作爲一個類的級別[屬性],但我發現在global.asax中註冊更靈活。

private void RegisterModelBinders() 
{ 
    ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder(); 
} 

然後創建modelbinder類,並編寫您的複雜驗證。您可以完全訪問對象上的所有屬性。這將在之後運行任何數據註釋已經運行,因此如果您想要顛倒任何驗證屬性的默認行爲,您總是可以清除模型狀態。

public class AddressModelBinder : DefaultModelBinder 
{ 
    protected override void OnModelUpdated(ControllerContext controllerContext, 
     ModelBindingContext bindingContext) 
    { 
     base.OnModelUpdated(controllerContext, bindingContext); 

     // get the address to validate 
     var address = (Address)bindingContext.Model; 

     // validate US zipcode 
     if (address.CountryCode == "US") 
     { 
      if (new Regex(@"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled). 
       Match(address.ZipOrPostal ?? "").Success == false) 
      { 
       // not a valid zipcode so highlight the zipcode field 
       var ms = bindingContext.ModelState;      
       ms.AddModelError(bindingContext.ModelName + ".ZipOrPostal", 
       "The value " + address.ZipOrPostal + " is not a valid zipcode"); 
      } 
     } 
     else { 
      // we don't care about the rest of the world right now 
      // so just rely on a [Required] attribute on ZipOrPostal 
     } 

     // all other modelbinding attributes such as [Required] 
     // will be processed as normal 
    } 
} 

這樣做的好處是,所有現有的驗證特性仍然可以工作 - [必需],[爲EmailValidator],[MyCustomValidator] - 無論你有。

您可以根據需要將任何額外的代碼添加到模型聯編程序和設置字段或模型級別的ModelState錯誤中。

請注意,我的Address是主力機型的孩子 - 在這種情況下CheckoutModel看起來像這樣:

public class CheckoutModel 
{ 
    // uses AddressModelBinder 
    public Address BillingAddress { get; set; } 
    public Address ShippingAddress { get; set; } 

    // etc. 
} 

這就是爲什麼我要做的bindingContext.ModelName+ ".ZipOrPostal"使模型誤差將被設置爲'BillingAddress.ZipOrPostal'和'ShippingAddress.ZipOrPostal'。

PS。來自'單元測試類型'的任何評論都很感謝。我不確定這對單元測試的影響。