从编辑的订单中缺少订单详细信息

时间:2016-09-26 16:21:02

标签: c# asp.net asp.net-mvc

为什么我使用空的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>

2 个答案:

答案 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对象只有orderIDcustomerID属性(以及从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移至您的表单,则不应再出现此问题。