【问题标题】:Validation propagating to related objects验证传播到相关对象
【发布时间】:2011-04-11 18:38:57
【问题描述】:

我有一个这样的模型:

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(下拉菜单,带有 <option value="Friend.ID">Friend.Name</option> 等选项

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

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

我将如何解决这个问题?当然,在这种情况下,我不想验证 Friend 的属性。我想过:

  1. 为我的视图使用 ViewModel,这会以某种方式解决我的问题。我还没有尝试过,因为我觉得我真的不需要解决这么简单的问题
  2. 将朋友的 ID 作为单独的参数 friend_id 发送,并手动绑定 Friend 属性。仅绑定了发布者的IDName 属性,我手动设置了Friend 属性。这涉及使用 friend_id 从我的存储库中获取 Friend 以使其成为“真正的”Person 对象。
  3. 检查 ModelState 并删除我知道不算数的错误。这是完全错误且不可扩展的(如果我添加例如 SecondFriend 属性,必须记住这样做

我觉得选项 2 是最可行的,但理想情况下我希望它是自动的。另外,我不能使用强类型帮助器,因为friend_id文本框的name属性必须与操作方法的参数名称匹配。

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

编辑

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

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

【问题讨论】:

  • 你不能在你的控制器方法上使用 bind 来防止模型绑定器尝试绑定/更新/验证朋友吗?
  • 是的,与解决方案 2 相结合是我想象的最终落脚点。我只是想知道我是否错过了一些重要的事情

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


【解决方案1】:

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

我一直在努力让这样的事情发挥作用。如果键名最好有VarName.PropertyName 这样的符号。在这种情况下它会起作用,因为你有两个不同的:

  • p.Name
  • p.Friend.Name

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

但是应该如何解决这个问题呢?一种方法是创建您自己的操作过滤器来执行模型验证并填充模型状态错误(首先将它们全部清除)。这也意味着您必须使用非 lambda 版本的 Html 扩展方法。因此,您必须使用 Html.TextBox 扩展名并提供您的自定义键,而不是 Html.TextBoxFor

为了让视图在视图方面更通用,您当然可以编写自己的 Html 扩展重载(您使用的那些),这将采用参数名称的附加参数,例如:

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

然后会生成

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

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

【讨论】:

  • 感谢您的回答,但如果我没看错,此解决方案用于分离/修复 Person.Name 字段和 Person.Friend.Name 字段的模型绑定。但是没有 Person.Friend.Name 字段,因为我想要的只是对 Person.Friend.ID 的引用,以便我知道它的主键(并且可以在数据库中设置关系)。我为朋友的 ID 使用文本框的例子可能是一个糟糕的例子。而是将其视为 value=Person.Friend.ID 的下拉列表。我目前使用 viewModels,因为我发现它有一些优势
【解决方案2】:

问题是您的PersonFriend 属于同一类型,并且它们在Name 属性上具有必需的属性。

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

您提供以下值:

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

但不是p.Friend.Name

解决方案

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

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

【讨论】:

  • 是的,您理解正确。这会起作用,但作为一个通用解决方案,它的可扩展性不是很高。在我的一个真实案例中,我有 3 个引用对象,它们都包含几个会触发验证错误的属性,并且为所有这些对象设置隐藏字段并不是很优雅)。
猜你喜欢
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 2022-06-13
  • 1970-01-01
  • 2015-10-01
  • 1970-01-01
相关资源
最近更新 更多