为什么我使用空的OrderDetails集合获取HttpPost Edit()
方法中的顺序?是否应该在ASP.NET 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>
答案 0 :(得分:1)
再多一点,因为我认为这里真正的“问题”是对MVC在移动应用程序时如何处理数据的误解 - 留下我之前的答案完整,因为那里有一些有用的信息,尽管我如果被问到,请高兴地放弃它。
首先,stephen.vakil对我原来答案的评论有一些有用的信息(强调我的):
您发布的对象中唯一的东西是模型绑定器可以从您的html字段映射的内容。 呈现视图后,您拥有的任何对象都不再存在于内存中。当您提交表单时,mvc API会将其在Request.Form中找到的任何内容与尝试将其与您的字段中的字段进行匹配模型。其他所有内容都留空(或者,在这种情况下,作为一个新的空HashSet)。 如果您希望OrderDetails在帖子中出现,请再次加载对象,或以某种方式保存数据(通过隐藏或其他方式)。
基本上,当您加载视图时,您正在执行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
属性(以及从HashSet
构造函数生成的空Order
) - 其他任何已加载到视图中的属性都会被删除,因为它不是提交数据的一部分。
如果您希望此数据保留在POST
请求中,您需要告知您的表单存储它。
有很多方法可以做到这一点,最简单的方法是@Html.HiddenFor()
。 HiddenFor
基本上只保存对特定属性的引用,因此它不会丢失,即使您不希望表单上显示该数据。您仍然可以显示数据选择,但除非明确告知您的表格,否则不会在帖子上传回信息。
采用相同的观点,让我们添加一些额外的属性:
@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
添加到模型中的其余属性中。现在,表单已明确告知属性,并将其传递到您的POST
请求中。因此,当POST
请求创建新的Order
对象时,它将具有这些属性。
请注意,对于复杂对象(例如Customer
,感谢@stephen muecke),您需要绑定各个属性以回发到表单 - 请参阅示例here。
因此,即使您不希望用户编辑部分或全部对象,您的控制器也会收到完全重新创建对象所需的所有数据。
将所有这些归结为一个句子,您的表单只会传回它明确告知要传回的数据 - 使用Hidden
或其他方法。
最后请注意,请理解您的@Html.Partial()
不是技术上是表单的一部分 - 它比这更复杂,但这是重要的一点。< / p>
如果您希望发布Partial
中的信息,则应考虑将其设为EditorTemplate
(在我的原始答案中以及本网站的其他地方进行了描述)。 EditorTemplate
本质上是一个奇特的表单字段,存储在那里的任何信息都将在POST
上传回。
答案 1 :(得分:0)
请注意这一点,但我的猜测是@Html.Partial
未将更新后的数据发回您的表单 - 有关其他信息,请参阅this question。基本上,MVC动态生成所有这些信息,而Partial
可能不会绑定回您的模型。
要发布此数据,您可能希望将此部分转换为EditorTemplate
并使用@Html.EditorFor(model => model.OrderDetails, "nameOfEditorTemplate")
。一旦更新,您应该看到OrderDetails
已正确填充。
编辑:
EditorTemplates
住在~/Views/Shared/EditorTemplates
。您真正需要做的就是将当前PartialView
移到那里并更新您的表单以使用EditorFor
代替Partial
。
编辑2:
看到您的_CreateOrEdit
视图,将其转换为Editor
,应该会在Order
操作中为ID
模型提供POST
。因此,在您的POST
方法中,您可能希望使用该ID调用GetOrder ByID()
并从那里获取OrderDetails
。
无论你选择哪条路线,你都没有在POST
上获取此信息的原因是你的模型无法绑定 - 它没有从View传递到Controller,因为它不是你表单的一部分。如果您将此Partial
设为Editor
或将OrderID
移至您的表单,则不应再出现此问题。