我有一个这样的模型:
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创建一个带有字段的表单
<option value="Friend.ID">Friend.Name</option>
发布表单时,我的控制器会接受一个Person对象(p
),该对象使用默认的模型绑定器绑定。为了明确,模型绑定器执行以下操作:ID
和Name
属性按预期绑定。 Friend
设置为新的Person
实例,其ID
等于我在下拉列表中选择的人的ID。 Friend.Name
的值为null
,因为我没有在表单中为其提供值。
问题:如果RequiredAttribute
文本框为空,我希望Name
触发它 - 而且确实如此。问题是它还会触发Friend
的name属性。因此,当我发布,填写所有字段时,我得到ModelState无效,错误是p.Friend.Name
是必需的。
我该如何解决这个问题?在这种情况下,我不想验证Friend
的属性。我想到了:
friend_id
发送,并手动绑定Friend
属性。仅绑定了发布人员的ID
和Name
属性,并手动设置Friend
属性。这涉及使用Friend
从我的存储库中获取friend_id
,使其成为“真正的”Person对象。SecondFriend
属性,则必须记住这样做我觉得选项2是最可行的,但理想情况下我希望它是自动的。此外,我无法使用强类型帮助器,因为friend_id
文本框'name
属性必须与操作方法的参数名称匹配。
我觉得有一点我错过了会让这更容易。希望我是对的。虽然我认为使用ViewModel有点乏味,但这是正确的做法,请告诉你。
修改的
现在使用ViewModels解决了问题,其中ID
,Name
和Friend_id
作为其属性,并且与Person
模型具有相同的验证属性。然后,我将ID
和Name
值映射到新的Person
实例。然后通过从存储库加载指定的朋友来设置Friend属性。与newPerson.Friend = repository.Get(viewModel.Friend_id)
当我得到时间时,我计划仔细查看AutoMapper“自动”执行此操作。
答案 0 :(得分:1)
问题是模型状态字典键只有属性名称作为键。因此,在您的情况下,其中有两个具有相同的密钥:Name
Person
和Name
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)
问题是,Person
和Friend
属于同一类型,并且Name
属性上有必需属性。
您提供以下值:
p.Name
p.ID
p.Friend.ID
但不是p.Friend.Name
。
我建议您在视图上添加一个额外的隐藏输入字段,并为其提供正确的值,或者如果您在服务器上不需要它,则只提供一些虚拟值(当您使用的只是朋友的ID时)。
这样,您的验证不会在p.Friend.Name
上中断。