我知道大多数人会建议我应该使用特定于我正在使用的表单的ViewModel,但我很好奇为什么我的子对象没有在TryUpdateModel上绑定。
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
@Html.HiddenFor(model => model.UserId)
@Html.HiddenFor(model => model.PrimaryAddress.AddressId)
<div class="editor-label">
@Html.LabelFor(model => model.PrimaryAddress.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PrimaryAddress.FirstName)
@Html.ValidationMessageFor(model => model.PrimaryAddress.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.PrimaryAddress.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PrimaryAddress.LastName)
@Html.ValidationMessageFor(model => model.PrimaryAddress.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UserName)
@Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.IsApproved)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.IsApproved)
@Html.ValidationMessageFor(model => model.IsApproved)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.IsEmployee)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.IsEmployee)
@Html.ValidationMessageFor(model => model.IsEmployee)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
控制器代码:
[HttpPost]
public ActionResult Edit(int id, FormCollection form)
{
var user = Token.DB.Users.Include("PrimaryAddress").Single(x => x.UserId == id);
if (TryUpdateModel(user, new string[] { "UserName", "Email", "IsApproved", "IsEmployee", "PrimaryAddress.FirstName", "PrimaryAddress.LastName" }))
{
try
{
Token.DB.SaveChanges();
return RedirectToAction("index");
}
catch (Exception ex)
{
while (ex.InnerException != null)
ex = ex.InnerException;
if (ex.Message.ToLowerInvariant().Contains("unique"))
ModelState.AddModelError("UserName", "UserName already exists");
}
}
return View(User);
}
代码不会抛出任何异常,它只是不填充表单中的user.PrimaryAddress.FirstName或user.PrimaryAddress.LastName。我想知道为什么?
我已经知道我可以使用特定的ViewModel解决问题并在后台映射信息。我也可以这样做:
<!-- Edit.cshtml -->
<div class="editor-field">
@Html.EditorFor(model => model.PrimaryAddress.FirstName, null, "FirstName")
@Html.ValidationMessageFor(model => model.PrimaryAddress.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.PrimaryAddress.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PrimaryAddress.LastName, null, "LastName")
@Html.ValidationMessageFor(model => model.PrimaryAddress.LastName)
</div>
// UsersController.cs
if (TryUpdateModel(user, new string[] { "UserName", "Email", "IsApproved", "IsEmployee"})
&& TryUpdateModel(user.PrimaryAddress, new string[] {"FirstName", "LastName" }))
所以真正的问题是为什么在第一个例子中它不具有约束力?
答案 0 :(得分:9)
所以真正的问题是为什么在第一个例子中它不具有约束力?
您的问题的答案非常简单:UpdateModel,TryUpdateModel或[Bind]
属性都不支持include / exclude属性列表中的“嵌套属性”。所以要正确地做事并使用视图模型。防止大规模属性分配攻击只是您应该使用视图模型的数百万个原因之一。好吧,您似乎通过执行第二个TryUpdateModel
找到了解决方法,但如果您在此域对象上有许多属性,则您的控制器操作代码可能很快变成意大利面条管道代码。
答案 1 :(得分:1)
我发现了一些我想做同样事情的案例。我还发现子对象不起作用,但子对象的列表/集合确实非常奇怪。
我发现了一个很好的link,描述了如何解决这个问题。
最终看起来像这样:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(int id, FormCollection collection)
{
User user = null;
if (id == 0)
{
user = new User();
UpdateModel(user, "User");
user.Contact = new Contact();
UpdateModel(user.Contact, "User.Contact");
user.Contact.Addresses = new EntitySet<Address>();
UpdateModel(user.Contact.Addresses, "User.Contact.Addresses");
}
else
{
// get current user object from DB, however you normally do this is fine.
user = userRepository.GetById(id);
UpdateModel(user, "User");
UpdateModel(user.Contact, "User.Contact");
UpdateModel(user.Contact.Addresses, "User.Contact.Addresses");
}
// at this point, model "user" and children would have been updated.
}
...