编辑过的订单中缺少订单细节

本文关键字:细节 单细节 单中缺 编辑 | 更新日期: 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对象只具有orderIDcustomerID属性(以及从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

编辑2:

看到你的_CreateOrEdit视图,把它变成Editor应该给你的Order模型一个ID上的POST动作。所以在你的POST方法中,你可能想用那个ID调用GetOrder ByID(),并从那里获得OrderDetails

无论你选择走哪条路,你没有在POST上得到这个信息的原因是你的模型没有什么可以绑定的-它没有从视图传递到控制器,因为它不是你表单的一部分。如果您将Partial改为Editor或将OrderID移动到表单中,则应该不再有此问题。