验证传播到相关对象

时间:2010-09-13 16:41:45

标签: asp.net-mvc validation model-binding

我有一个这样的模型:

public class Person
{
    public int ID { get; set; }

    [Required(ErrorMessage="Name cant be empty")]
    public string Name { get; set; }

    public Person Friend { get; set; }
}

我想创建一个新的Person,并使用强类型的HtmlHelper创建一个带有字段的表单

        
  • ID
  •     
  • 名称
  •     
  • 朋友ID(使用<option value="Friend.ID">Friend.Name</option>
  • 等选项下拉列表

alt text

发布表单时,我的控制器会接受一个Person对象(p),该对象使用默认的模型绑定器绑定。为了明确,模型绑定器执行以下操作:IDName属性按预期绑定。 Friend设置为新的Person实例,其ID等于我在下拉列表中选择的人的ID。 Friend.Name的值为null,因为我没有在表单中为其提供值。

问题:如果RequiredAttribute文本框为空,我希望Name触发它 - 而且确实如此。问题是它还会触发Friend的name属性。因此,当我发布,填写所有字段时,我得到ModelState无效,错误是p.Friend.Name是必需的。 alt text

我该如何解决这个问题?在这种情况下,我不想验证Friend的属性。我想到了:

  1. 将ViewModels用于我的视图,这将以某种方式解决我的问题。我还没有尝试过,因为我觉得我真的不需要这么简单的问题
  2. 将朋友的ID作为单独的参数friend_id发送,并手动绑定Friend属性。仅绑定了发布人员的IDName属性,并手动设置Friend属性。这涉及使用Friend从我的存储库中获取friend_id,使其成为“真正的”Person对象。
  3. 检查ModelState并删除我知道的错误。这只是完全错误且不可扩展(如果我添加例如SecondFriend属性,则必须记住这样做
  4. 我觉得选项2是最可行的,但理想情况下我希望它是自动的。此外,我无法使用强类型帮助器,因为friend_id文本框'name属性必须与操作方法的参数名称匹配。

    我觉得有一点我错过了会让这更容易。希望我是对的。虽然我认为使用ViewModel有点乏味,但这是正确的做法,请告诉你。

    修改

    现在使用ViewModels解决了问题,其中IDNameFriend_id作为其属性,并且与Person模型具有相同的验证属性。然后,我将IDName值映射到新的Person实例。然后通过从存储库加载指定的朋友来设置Friend属性。与newPerson.Friend = repository.Get(viewModel.Friend_id)

    一样

    当我得到时间时,我计划仔细查看AutoMapper“自动”执行此操作。

2 个答案:

答案 0 :(得分:1)

问题是模型状态字典键只有属性名称作为键。因此,在您的情况下,其中有两个具有相同的密钥:Name PersonName Friend

我一直在努力做出像这样的工作。如果关键名称的符号类似VarName.PropertyName,那将是最好的。在这种情况下它会起作用,因为你有两个不同的:

  • p.Name
  • p.Friend.Name

ClassName.PropertyName不起作用,因为你可以有两个相同类型但名称不同的动作参数。

但是应该如何解决这个问题呢?一种方法是创建自己的Action过滤器,用于模型验证和填充模型状态错误(首先清除所有错误)。这也意味着你必须使用非lambda版本的Html扩展方法。因此,您必须使用Html.TextBoxFor扩展名并提供自定义密钥,而不是Html.TextBox

为了在视图方面使事情变得更通用,您当然可以编写自己的Html扩展重载(您使用的重载),它将采用参数名称的附加参数,如:

Html.TextBoxFor("p", model => model.Friend.Name);

然后生成

<input type="text" name="p.Friend.Name" id="p_Friend_Name" />

相信我,默认模型绑定器将能够使用这些输入名称。它具有参数名称及其属性的知识。

答案 1 :(得分:1)

问题是,PersonFriend属于同一类型,并且Name属性上有必需属性。

只是为了确保我理解这个问题

您提供以下值:

  • p.Name
  • p.ID
  • p.Friend.ID

但不是p.Friend.Name

解决方案

我建议您在视图上添加一个额外的隐藏输入字段,并为其提供正确的值,或者如果您在服务器上不需要它,则只提供一些虚拟值(当您使用的只是朋友的ID时)。

这样,您的验证不会在p.Friend.Name上中断。