9

我的页面上有某些面板在某些情况下是隐藏的。

例如,我可能有一个“帐单地址”和“送货地址”,如果选中了“ShippingSameAsBilling”复选框,我不想验证“送货地址”。

我正在尝试使用 ASP.NET MVC 2(预览版 1)的新DataAnnotations 功能来实现这一点。

我需要防止在未显示“送货地址”时对其进行验证,并且需要找到实现此目的的方法。我说的主要是服务器端,而不是使用 jquery

我怎样才能做到这一点?我有几个与自定义模型绑定相关的想法,但我目前的最佳解决方案如下。对此方法有任何反馈吗?

4

7 回答 7

6

对于 CheckoutModel 我正在使用这种方法(大多数字段隐藏):

[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{        
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }
    public bool ShipToBillingAddress { get; set; }
}

public class Address
{
    [Required(ErrorMessage = "Email is required")]
    public string Email { get; set; }

    [Required(ErrorMessage = "First name is required")]
    public string FirstName { get; set; }

    [Required()]
    public string LastName { get; set; }

    [Required()]
    public string Address1 { get; set; }
}

如果发现任何以“ShippingAddress”开头的字段,自定义模型绑定器将删除所有 ModelState 错误。然后“TryUpdateModel()”将返回 true。

    public class CheckoutModelBinder : DefaultModelBinder
    {
        protected override void OnModelUpdated(ControllerContext controllerContext,
                                               ModelBindingContext bindingContext) {

            base.OnModelUpdated(controllerContext, bindingContext);

            var model = (CheckoutModel)bindingContext.Model;

            // if user specified Shipping and Billing are the same then 
            // remove all ModelState errors for ShippingAddress
            if (model.ShipToBillingAddress)
            {
                var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
                foreach (var key in keys)
                {
                    bindingContext.ModelState.Remove(key);
                }
            }
        }    
    }

有更好的解决方案吗?

于 2009-08-30T03:56:07.427 回答
3

http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html

于 2009-08-30T04:00:48.477 回答
2

我能看出你的困境。我也在寻找其他验证解决方案,这些验证规则可能适用于给定模型对象上的多个属性,甚至对象图中不同模型对象的许多属性(如果您不幸验证链接对象)像这样)。

接口的限制IDataErrorInfo是模型对象仅在没有属性有错误时满足有效状态。这就是说一个有效的对象是它的所有属性也是有效的。但是,我可能会遇到这样一种情况,如果属性 A、B 和 C 有效 - 那么整个对象都是有效的。而且如果属性 A 无效但 BC 有效,那么该对象满足有效性。我根本无法用IDataErrorInfo接口/DataAnnotations属性来描述这个条件/规则。

所以我找到了这种委托方法。现在,在撰写本文时,MVC 中的许多有用的进步并不存在,但核心概念应该对您有所帮助。我们创建委托函数来验证更复杂的需求,而不是使用属性来定义对象的验证条件,并且因为它们是委托的,所以我们可以重用它们。当然它的工作量更大,但是使用委托意味着我们应该能够编写一次验证规则代码并将所有验证规则存储在一个地方(可能是服务层)(kool 位)甚至使用 MVC 2DefaultModelBinder来调用自动验证(无需大量检查我们的控制器操作 - 就像 Scott 的博客说我们可以使用DataAnnotations. 参考“强类型 UI 助手”标题之前的最后一段)!

我相信您可以使用匿名委托(例如Func<T>或)来加强上面文章中建议的方法,Predicate<T>并为验证规则编写自定义代码块将启用跨属性条件(例如,如果您的ShippingSameAsBilling属性在哪里,您提到的条件是true 那么你可以忽略更多关于送货地址等的规则)。

DataAnnotations用于使对象的简单验证规则非常容易,只需很少的代码。但是随着您的需求的发展,您将需要验证更复杂的规则。MVC2 模型绑定器中的新虚拟方法应该继续为我们提供将我们未来的验证发明集成到 MVC 框架中的方法。

于 2009-10-28T20:41:32.167 回答
2

确保您不想验证的字段未发布到操作中。我们只验证实际发布的字段。

编辑:(由提问者)

这种行为在 MVC2 RC2 中发生了变化:

默认验证系统验证整个模型 ASP.NET MVC 1.0 和 RC 2 之前的 ASP.NET MVC 2 预览版中的默认验证系统仅验证发布到服务器的模型属性。在 ASP.NET MVC 2 中,新行为是在验证模型时验证所有模型属性,而不管是否发布了新值。依赖于 ASP.NET MVC 1.0 行为的应用程序可能需要更改。有关此更改的更多信息,请参阅Brad Wilson 博客上的 ASP.NET MVC 中的输入验证与模型验证条目。

于 2009-10-29T00:04:05.863 回答
1

对于更复杂的情况,我从简单的 DataAnnotations 转移到以下内容:使用访问者和扩展方法进行验证

如果您想使用您的 DataAnnotations,您可以替换如下内容:

public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{   
    // snip... 
    if (string.IsNullOrEmpty (payment.CCName))
    {
      yield return new ErrorInfo ("CCName", "Credit card name is required");
    }
}

使用通过 DataAnnotations(我没有 atm)按名称验证属性的方法。

于 2009-11-10T19:30:14.150 回答
1

我创建了一个仅验证提交的密钥的部分模型绑定器。出于安全原因(如果我要更进一步),我将创建一个数据注释属性,用于标记允许从模型中排除哪些字段。然后,OnModelUpdated 检查字段属性以确保不存在不希望的过帐情况。

public class PartialModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        // default model binding to get errors
        base.OnModelUpdated(controllerContext, bindingContext);

        // remove errors from filds not posted
        // TODO: include request files
        var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
        var unpostedKeysWithErrors = bindingContext.ModelState
            .Where(i => !postedKeys.Contains(i.Key))
            .Select(i=> i.Key).ToList();
        foreach (var key in unpostedKeysWithErrors)
        {
            bindingContext.ModelState.Remove(key);
        }
    }    
}
于 2010-10-19T19:39:01.650 回答
0

这与DataAnnotations无关,但您看过Fluent Validation项目吗?它为您提供了对验证的细粒度控制,如果您有对象到对象的验证,则两个对象的聚合对象将使您继续前进。

此外,它似乎是在考虑 MVC 的情况下构建的,但它也有自己的“运行时”,因此您也可以在其他 .NET 应用程序中使用它,这是我书中的另一个好处。

于 2009-11-10T18:38:49.933 回答