验证传播到相关对象

发布于 2024-09-18 13:19:16 字数 2166 浏览 6 评论 0 原文

我有一个这样的模型:

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

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

    public Person Friend { get; set; }
}

朋友 ID制作一个包含字段的表单

  • 我想创建一个新的 Person,并使用强类型 HtmlHelper ID
  • 名称
  • (带有

alt text

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

问题: 我想要 >RequiredAttributeName 文本框为空时触发 - 问题是它也会在 Friend 的 name 属性上触发。因此,当我发布并填写所有字段时,我发现 ModelState 无效,错误是 p.Friend.Name 是必需的。 alt text

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

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

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

。我错过了,希望我是对的,虽然我认为使用 ViewModel 有点乏味,但这是正确的做法,请告诉我们

现在

已经解决了问题 。使用具有 IDNameFriend_id 作为属性的 ViewModel,以及与 Person 模型相同的验证属性。然后将 IDName 值映射到新的 Person 实例,然后通过从存储库加载指定的好友来设置 Friend 属性。就像 newPerson.Friend = repository.Get(viewModel.Friend_id)

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

I have a model like this:

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

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

    public Person Friend { get; set; }
}

I want to create a new Person, and made a form with the fields using the strongly typed HtmlHelper

  • ID
  • Name
  • Friend ID (dropdown with options like <option value="Friend.ID">Friend.Name</option>

alt text

When posting the form, my controller takes in a Person object (p), which is bound using the default modelbinder. Just to be explicit, the modelbinder does the following: The ID and Name properties are bound as expected. The Friend is set to a new Person instance whose ID equals the ID of the person I chose in the dropdown. The Friend.Name's value is null because I didn't provide a value for it in the form.

The problem: I want the RequiredAttribute to fire on the Name textbox if it is empty - and it does. The problem is that it also fires on the Friend's name attribute. So when I post, filling in all the fields, I get that the ModelState is invalid and the error being that p.Friend.Name is required.
alt text

How would I solve this problem? Of couse in this case I don't want to validate the Friend's properties. I've thought of:

  1. Using ViewModels for my views, which will somehow solve my problems. I haven't tried this yet as I feel I really shouldn't need to for such a simple problem
  2. Sending the friend's ID as a separate parameter, friend_id, and binding the Friend property manually. Only the ID and Name attributes of the posted person is bound and I manually set the Friend property. This involves getting the Friend from my repository using the friend_id to make it a "real" Person object.
  3. Checking the ModelState and removing errors I know don't count. This is just plain wrong and non-scalable (have to remember to do this if I add for example a SecondFriend property

I feel option 2 is the most feasable, but ideally I'd like it to be automatic. In addition, I can't use the strongly typed helper, as the friend_id textbox' name attribute must match the action method's parameter name.

I feel there's some point I've missed that would make this easier. Hopefully I'm right. Although I think it's a bit tedious using a ViewModel, it that's the correct thing to do, please do tell.

Edit

For now solved the problem using ViewModels with ID, Name and Friend_id as its properties and the same validation attributes as the Person model. I then map the ID and Name values over to a new Person instance. The Friend property is then set by loadning the specified friend from the repository. Like newPerson.Friend = repository.Get(viewModel.Friend_id)

When I get the time I plan on looking closer at AutoMapper to do this "automatically".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

墨落成白 2024-09-25 13:19:17

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

我一直在努力让这样的事情发挥作用。如果键名称具有类似于 VarName.PropertyName 的表示法,那就最好了。在这种情况下它会起作用,因为你有两个不同的:

  • p.Name
  • p.Friend.Name

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

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

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

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

然后将生成

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

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

The problem is that model state dictionary keys only have property name as the key. So in your case there are two of them that have the same key: Name on Person and Name on Friend.

I've always struggled to make things like this work. It would be best if key names would have notation like VarName.PropertyName. In this case it would work, since you'd have two different ones:

  • p.Name
  • p.Friend.Name

Having ClassName.PropertyName wouldn't work, since you could have two action parameters of the same type but with different name.

But how should one tackle this problem? One way would be to create your own Action filter that does model validation and fill model state errors (by first clearing them all). It would also mean that you'd have to use the non-lambda versions of Html extension methods. So instead of Html.TextBoxFor, you'd have to use Html.TextBox extension and provide your custom keys.

To make things more generic on the view side, you could of course write your own Html extension overloads (the ones that you use) that would take an additional parameter of parameter name like:

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

That would then generate

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

And believe me, default model binder will be able to consume these kind of input names. It has the knowledge of parameter names and their properties.

棒棒糖 2024-09-25 13:19:17

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

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

您提供以下值:

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

但不是 <代码>p.Friend.Name

解决方案

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

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

The problem is that both your Person and Friend are of the same type, and they have a required attribute on Name property.

Just to make sure I understand this question

You're providing following values:

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

but not p.Friend.Name.

Solution

I suggest you put an additional hidden input field on your view and provide either the correct value for it or just some dummy value if you don't need it on the server (when all you'll use is friend's ID).

This way your validation won't break on p.Friend.Name.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文