编辑过的订单中缺少订单细节
本文关键字:细节 单细节 单中缺 编辑 | 更新日期: 2023-09-27 18:09:05
为什么我得到HttpPost Edit()
方法内的订单与空OrderDetails集合?它在ASP中是预期的吗?净MVC吗?
谢谢。
订单控制器:
public ActionResult Edit(int id)
{
Order order = GetOrderById(id);
return View(order);
}
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
context.Entry(order).State = EntityState.Modified;
context.SaveChanges();
// HERE order.OrderDetails is empty even if it was filled with a number of items
}
return View(order);
}
private Order GetOrderById(int id)
{
return context.Orders.Single(x => x.orderID == id);
}
模型:
public partial class Order
{
public Order()
{
this.OrderDetails = new HashSet<OrderDetail>();
}
public int orderID { get; set; }
public int customerID { get; set; }
public string promoCodeID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public virtual PromoCode PromoCode { get; set; }
}
编辑:
视图:
@using Corporate.Models
@model Corporate.Models.Order
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm("Edit", "Orders", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.Partial("_CreateOrEdit", Model)
<br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Update" />
</div>
</div>
@Html.HiddenFor(model => model.taxes)
}
部分_CreateOrEdit
充满了像这样的东西,没有别的:
<div class="form-group">
@Html.LabelFor(m => m.orderID, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.orderID, new { @class = "form-control datepicker"})
@Html.ValidationMessageFor(m => m.orderID)
</div>
</div>
再试一试,因为我希望这里真正的"问题"是对MVC在应用程序中如何处理数据的误解-留下我之前的答案,因为那里有一些有用的信息,尽管如果被问到我会很乐意放弃它。
首先,stephen。Vakil对我最初的回答的评论有一些有用的信息(强调我的):
张贴对象中唯一的东西是模型绑定器可以从html字段映射的东西。一旦你的视图被渲染,你拥有的任何对象都不再在内存中。当你提交表单时,mvc API将接受它在Request中找到的任何内容。表单,并尝试将其与模型中的字段进行匹配。其他所有内容都留空(或者,在本例中,作为一个新的空HashSet)。如果你想让你的OrderDetails在post上存在,要么再次加载对象,要么以某种方式保存数据(通过隐藏或其他方式)。
基本上,当你加载一个视图,你正在执行一个GET
请求。在您的示例中,GET
方法接受订单ID:
public ActionResult Edit(int id)
{
Order order = GetOrderById(id);
return View(order);
} //get the order by ID, and pass it down to the view
有了这个GET
请求,您的View
页面将保存您期望的数据(假设为给定的ID找到了订单),并且假设您的视图为它设置了,您应该能够看到OrderDetails
-所以这里一切都很好。
注意,在这一点上,您的GET
请求已经完成了它的职责,并将它持有的Order
对象扔到一边-就控制器而言,它不再有任何用处了。
对于下一个位,这里有一个简化的视图:
@using Corporate.Models
@model Corporate.Models.Order
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm("Edit", "Orders", FormMethod.Post))
{
@Html.TextBoxFor(model => model.orderID)
@Html.TextBoxFor(model => model.customerID)
<input type="submit" value="Submit" />
}
当您在此视图上单击Submit
时,您的POST
方法被调用:
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
context.Entry(order).State = EntityState.Modified;
context.SaveChanges();
// HERE order.OrderDetails is empty even if it was filled with a number of items
}
return View(order);
}
当我们到达这里时,一个新的Order
对象是从表单中的数据创建的!在简化视图的情况下,这意味着新的Order
对象只具有orderID
和customerID
属性(以及从Order
构造函数生成的空HashSet
) - 加载到视图中的任何其他内容都将被删除,因为它不是提交数据的一部分。
将所有数据返回给控制器
如果你想要这个数据保存到POST
请求中,你需要告诉你的表单来存储它
有很多方法可以做到这一点,可能最简单的是@Html.HiddenFor()
。HiddenFor
本质上只是保存对特定属性的引用,因此它不会丢失,即使您不希望该数据在表单上可见。你仍然可以按照你选择的方式显示数据,但是除非你的表单被明确告知,否则它不会在post时传递信息。
使用相同的视图,让我们添加一些附加属性:
@using Corporate.Models
@model Corporate.Models.Order
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm("Edit", "Orders", FormMethod.Post))
{
@Html.TextBoxFor(model => model.orderID)
@Html.TextBoxFor(model => model.customerID)
<input type="submit" value="Submit" />
//these values will all be POSTed back exactly as they were passed down in the GET request
@Html.HiddenFor(model => model.promoCodeID)
@Html.HiddenFor(model => model.Customer.Prop1)
@Html.HiddenFor(model => model.Customer.Prop2) //etc - these objects need all of their properties bound to the form
@Html.HiddenFor(model => model.OrderDetails.Prop1)
@Html.HiddenFor(model => model.PromoCode.Prop1)
}
注意,我已经将HiddenFor
s添加到模型中的其余属性中。现在,表单已经被显式地告知了属性,并将它们传递到POST
请求中。所以当POST
请求创建一个新的Order
对象时,它将有这些属性来工作。
请注意,对于复杂的对象(例如Customer
,感谢@stephen muecke),您需要绑定单个属性以发送回表单-参见这里的示例。
有了这个,你的控制器将接收到它需要的所有数据来完全重建对象,即使你不希望用户编辑它的部分或全部。
将所有这些归结为一句话,您的表单将只传递回它明确告诉传递回的数据-通过使用Hidden
或其他方法。
最后要说明的是,请理解您的@Html.Partial()
在技术上不是您表单的一部分-它比这更复杂,但这是重要的一点。
如果你想要发布你的Partial
中的信息,你应该考虑将其设置为EditorTemplate
(在我最初的回答中描述,以及本网站的其他地方)。EditorTemplate
本质上是一个奇特的表单字段,存储在那里的任何信息都将传递回POST
。
对此持保留意见,但我的猜测是您的@Html.Partial
没有将更新的数据发布回您的表单-请参阅此问题以获取更多信息。基本上,MVC动态生成所有这些信息,Partial
可能不会绑定回你的模型。
对于要发布的数据,您可能希望将该部分转换为EditorTemplate
并使用@Html.EditorFor(model => model.OrderDetails, "nameOfEditorTemplate")
。更新后,您应该看到OrderDetails
被正确填充。
EditorTemplates
live in ~/Views/Shared/EditorTemplates
。你真正需要做的就是将当前的PartialView
移到那里,并更新你的表单,使用EditorFor
而不是Partial
。
看到你的_CreateOrEdit
视图,把它变成Editor
应该给你的Order
模型一个ID
上的POST
动作。所以在你的POST
方法中,你可能想用那个ID调用GetOrder ByID()
,并从那里获得OrderDetails
。
无论你选择走哪条路,你没有在POST
上得到这个信息的原因是你的模型没有什么可以绑定的-它没有从视图传递到控制器,因为它不是你表单的一部分。如果您将Partial
改为Editor
或将OrderID
移动到表单中,则应该不再有此问题。