4

我有一个简单的地址输入应用程序,我正在尝试使用 IDataErrorInfo 接口,如asp.net 站点上所述

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

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

4

3 回答 3

5

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

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

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

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

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

希望这可以帮助。

于 2009-10-02T20:06:37.993 回答
1

这是我为超越简单数据注释模型的更复杂验证找到的最佳解决方案。

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

我碰巧遇到了和提问者完全相同的问题。我想验证美国邮政编码,但前提是国家/地区被选为美国。显然,模型级数据注释不会有任何好处,因为这不会导致邮政编码以红色突出显示为错误。[可以在 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 错误中。

请注意,对我来说 anAddress是主模型的子模型 - 在这种情况下CheckoutModel,它看起来像这样:

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

    // etc.
}

这就是为什么我必须这样做,以便为“BillingAddress.ZipOrPostal”和“ShippingAddress.ZipOrPostal”设置模型错误。bindingContext.ModelName+ ".ZipOrPostal"

PS。来自“单元测试类型”的任何评论表示赞赏。我不确定这对单元测试的影响。

于 2010-01-17T12:42:59.247 回答
0

关于错误字符串、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 之间的命名约定。

于 2009-10-03T02:00:09.950 回答