2009-10-02 69 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。来自'单元测试类型'的任何评论都很感谢。我不确定这对单元测试的影响。