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
名称
(带有
发布表单时,我的控制器接收一个 Person 对象(p),使用默认的 modelbinder 进行绑定。明确地说,modelbinder 执行以下操作:ID 和 Name 属性是按预期绑定。 Friend 设置为新的 Person 实例,其 ID 等于我在下拉列表中选择的人员的 ID。 code>Friend.Name 的值为 null 因为我没有在表单中提供它的值
问题: 我想要 >RequiredAttribute 在 Name 文本框为空时触发 - 问题是它也会在 Friend 的 name 属性上触发。因此,当我发布并填写所有字段时,我发现 ModelState 无效,错误是 p.Friend.Name 是必需的。
已经解决了问题 。使用具有 ID、Name 和 Friend_id 作为属性的 ViewModel,以及与 Person 模型相同的验证属性。然后将 ID 和 Name 值映射到新的 Person 实例,然后通过从存储库加载指定的好友来设置 Friend 属性。就像 newPerson.Friend = repository.Get(viewModel.Friend_id)
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>
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.
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:
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
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.
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".
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:
问题是您的 Person 和 Friend 属于同一类型,并且它们在 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.
发布评论
评论(2)
问题是模型状态字典键只有属性名称作为键。因此,在您的情况下,其中有两个具有相同的密钥:
Person
上的Name
和Friend
上的Name
。我一直在努力让这样的事情发挥作用。如果键名称具有类似于
VarName.PropertyName
的表示法,那就最好了。在这种情况下它会起作用,因为你有两个不同的:p.Name
p.Friend.Name
拥有
ClassName.PropertyName
不会不起作用,因为您可以有两个类型相同但名称不同的操作参数。但应该如何解决这一问题呢?一种方法是创建您自己的操作过滤器来执行模型验证并填充模型状态错误(首先将其全部清除)。这也意味着您必须使用 Html 扩展方法的非 lambda 版本。因此,您必须使用
Html.TextBox
扩展并提供自定义键,而不是Html.TextBoxFor
。为了使视图方面的内容更加通用,您当然可以编写自己的 Html 扩展重载(您使用的扩展重载),该重载将采用参数名称的附加参数,例如:
然后将生成
并且相信我,默认模型绑定器将能够使用这些类型的输入名称。它具有参数名称及其属性的知识。
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
onPerson
andName
onFriend
.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 useHtml.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:
That would then generate
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.
问题是您的
Person
和Friend
属于同一类型,并且它们在Name
属性上具有必需的属性。只是为了确保我理解这个问题
您提供以下值:
p.Name
p.ID
p.Friend.ID
但不是 <代码>p.Friend.Name。
解决方案
我建议您在视图上放置一个额外的隐藏输入字段,并为其提供正确的值,或者如果您在服务器上不需要它(当您将使用的只是朋友的 ID 时),则仅提供一些虚拟值。
这样您的验证就不会在
p.Friend.Name
上中断。The problem is that both your
Person
andFriend
are of the same type, and they have a required attribute onName
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
.