【问题标题】:using IDataErrorInfo in asp.net mvc在 asp.net mvc 中使用 IDataErrorInfo
【发布时间】:2009-10-02 19:55:58
【问题描述】:

我有一个简单的地址输入应用程序,我正在尝试将 IDataErrorInfo 接口用作explained on the asp.net site

它适用于可以独立验证的项目,但在某些项目依赖于其他项目时效果不佳。例如,验证邮政编码取决于国家/地区:

    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 中。

【问题讨论】:

    标签: asp.net-mvc validation idataerrorinfo


    【解决方案1】:

    对于更复杂的业务规则验证,而不是类型验证,实现服务层等设计模式可能更好。您可以检查 ModelState 并根据您的逻辑添加错误。

    您可以在此处查看 Rob Conroys 模式示例

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

    这篇关于数据注释的文章也很有用。

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

    希望这会有所帮助。

    【讨论】:

    【解决方案2】:

    这是我发现的最佳解决方案,可用于超越简单数据注释模型的更复杂验证。

    我确信我不是唯一一个尝试实现IDataErrorInfo 的人,并且看到它只为我创建了两种实现方法。我在想等一下——我现在是否必须进去为所有事情从头开始编写我自己的自定义例程?而且 - 如果我有模型级别的东西要验证怎么办。除非您想在 IDataErrorInfo 实现中执行 like thisthis 的操作,否则当您决定使用它时,您似乎只能靠自己了。

    我碰巧遇到了和提问者完全相同的问题。我想验证美国邮政编码,但前提是国家/地区被选为美国。显然,模型级别的数据注释不会有任何好处,因为这不会导致邮政编码以红色突出显示为错误。 [可以在 PropertiesMustMatchAttribute 类的 MVC 2 示例项目中找到类级别数据注释的好示例]。

    解决方法很简单:

    首先你需要在 global.asax 中注册一个 modelbinder。如果需要,您可以将其作为类级别 [attribute] 执行,但我发现在 global.asax 中注册更加灵活。

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

    然后创建模型绑定器类,并编写您的复杂验证。您可以完全访问对象的所有属性。这将在任何数据注释运行后运行,因此如果您想反转任何验证属性的默认行为,您始终可以清除模型状态。

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

    这样做的好处在于,您现有的所有验证属性仍然有效 - [Required]、[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。任何来自“单元测试类型”的 cmets 都表示赞赏。我不确定这对单元测试的影响。

    【讨论】:

      【解决方案3】:

      关于错误字符串、IDataErrorInfo 和 Html.ValidationMessage 的注释,您可以使用以下方法显示对象级别与字段级别的错误消息:

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

      在您的控制器中,使用 [Bind(Prefix = "address")] 装饰对象的 post 方法处理程序参数。在 HTML 中,将输入字段命名为...

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-25
        • 2011-06-19
        • 1970-01-01
        • 1970-01-01
        • 2020-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多