我模拟的业务场景:内包含多个商品的购物订单的增/删/查/改,我要问的主要是添加(增);
OrderHead Model:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.ComponentModel.DataAnnotations; 6 7 namespace Test.Models 8 { 9 public class OrderHead 10 { 11 public int Id { get; set; } 12 13 [Required] 14 [Display(Name = "订单号")] 15 public string OrderNum { get; set; } 16 17 public virtual ICollection<OrderDetail> OrderDetails { get; set; } 18 } 19 }
OrderDetail Model:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.ComponentModel.DataAnnotations; 6 7 namespace Test.Models 8 { 9 public class OrderDetail 10 { 11 public int Id { get; set; } 12 13 [Required] 14 [Display(Name = "商品")] 15 public string Commodity { get; set; } 16 17 public decimal Qty { get; set; } 18 19 public decimal Amount { get; set; } 20 21 //订单内码 22 public int OrderHeadId { get; set; } 23 24 //订单导航属性 25 public OrderHead OrderHead { get; set; } 26 } 27 }
Order ViewModel:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using FreightOrderTraceability.Models; 6 using System.Web.Mvc; 7 8 namespace Test.ViewModels 9 { 10 public class OrderViewModel 11 { 12 public OrderHead OrderHead { get; set; } 13 14 public OrderDetail OrderDetail { get; set; } 15 } 16 }
CreateOrder Controllers:
1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 using System.Data.Entity; 5 using System.Linq; 6 using System.Net; 7 using System.Web; 8 using System.Web.Mvc; 9 using Test.Models; 10 using Test.ViewModels; 11 12 namespace Test.Controllers 13 { 14 public class OrdersController : Controller 15 { 16 private DBContext db = new DBContext(); 17 18 // GET: Orders/CreateHead 19 public ActionResult CreateHead() 20 { 21 OrderViewModel createViewModel = new OrderViewModel();
24 return View(createViewModel); 25 } 26 27 [HttpPost] 28 [ValidateAntiForgeryToken] 29 public ActionResult CreateHead(OrderViewModel createViewModel) 30 {
33 if (ModelState.IsValid) 34 { 35 db.OrderHeads.Add(createViewModel.OrderHead); 36 db.SaveChanges(); 37 return RedirectToAction("Index"); 38 } 39 40 return View(createViewModel); 41 } 42 43 // GET: Orders/CreateDetail/5 44 public ActionResult CreateDetail(int? id) 45 { 46 if (id == null) 47 { 48 return new HttpStatusCodeResult(HttpStatusCode.NotFound); 49 } 50 CreateViewModel createViewModel = new CreateViewModel(); 51 createViewModel.OrderHead = db.OrderHeads.Find(id); 52 if (createViewModel.OrderHead == null) 53 { 54 return new HttpStatusCodeResult(HttpStatusCode.NotFound); 55 } 56 return View(createViewModel); 57 } 58 59 [HttpPost] 60 [ValidateAntiForgeryToken] 61 public ActionResult CreateDetail(OrderViewModel createViewModel) 62 { 63 if (ModelState.IsValid) 64 { 65 createViewModel.OrderDetail.OrderHeadId = createViewModel.OrderHead.Id; 66 db.OrderDetails.Add(createViewModel.OrderDetail); 67 db.SaveChanges(); 68 return RedirectToAction("Index"); 69 } 70 return View(createViewModel); 71 } 72 73 protected override void Dispose(bool disposing) 74 { 75 if (disposing) 76 { 77 db.Dispose(); 78 } 79 base.Dispose(disposing); 80 } 81 } 82 }
涉及问题的添加商品明细 CreateDetail View:
1 @model Test.ViewModels.OrderViewModel 2 3 @{ 4 ViewBag.Title = "添加"; 5 ViewBag.Content = "购物订单明细"; 6 Layout = "~/Views/Shared/_Layout.cshtml"; 7 } 8 9 <h2>@ViewBag.Title</h2> 10 <h4>@ViewBag.Content</h4> 11 <hr /> 12 13 @using (Html.BeginForm()) 14 { 15 <dl class="dl-horizontal"> 16 <dt>@Html.DisplayNameFor(model => model.OrderHead.OrderNum)</dt> 17 <dd>@Html.DisplayFor(model => model.OrderHead.OrderNum)</dd> 30 </dl> 31 <hr /> 32 <div class="row"> 33 @Html.AntiForgeryToken() 34 <div class="form-horizontal"> 35 @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 36 <div class="form-group"> 37 @Html.LabelFor(model => model.OrderDetail.Commodity) 39 @Html.EditorFor(model => model.OrderDetail.Commodity) 58 </div> 59 </div> 60 </div> 61 <hr /> 62 <div class="form-group"> 63 <input type="submit" value="保存"/> 65 </div> 66 }
没有使用SPA页面配合jQuery来进行数据库操作,采用MVC原生机制,所以添加一个新的购物订单的操作分为2个页面进行:
1.先添加OrderHead的订单信息:单据号和内码,这个没有问题
2.后添加OrderDetail的商品信息:商品名称、数量、金额并匹配对应的Order的内码,这个有问题;
问题过程:
1.假设 OrderHead 已保存(自动生成的id=5)
2.进入 OrderDetail 添加页面(Orders/CreateDetail/5),添加OrderDetail的明细数据,需关联OrderHead的数据(id=5)
3.进入CreateDetail 添加页面,此时传入的 createViewModel.OrderHead 有值,但等到完成OrderDetail页面的数据(商品、数量、金额)填写后,传回Controllers的ViewModel里createViewModel.OrderHead显示null,createViewModel.OrderDetail有值;
请问:createViewModel.OrderHead的值如何通过ViewModel传入并回传到Controllers?
黄色部分代码执行错误!
注:为简洁,部分冗余无关代码已清楚,请别在意行号;
都用mvc了。还没有请求响应模型的概念。先弄清楚请求响应的概念就不会有这些乱七八糟的问题了
我来回答下吧。
在这个Action中:
public ActionResult CreateDetail(int? id)
你构造了 CreateViewModel 的实例,并且初始化了 OrderHead 属性:
createViewModel.OrderHead = db.OrderHeads.Find(id);
然而,你没有初始化 它的 OrderDetail 属性,这使得在跳转到View页面后,CreateViewModel 的 OrderDetail 属性为null值,自然无法通过 @Html 的扩展方法为 OrderDetail 的下级属性赋值(model => model.OrderDetail.Commodity),然后在提交表单时,OrderDetail 仍保持为 null 传到你的Action方法,这就表现出传值失败。
要解决这个问题,需要在CreateDetail(int? id)方法中,初始化CreateView的OrderDetail属性。
但是我实际测试时是回传时 OrderDetail 有值,OrderHead无值.
目前测试下来的问题在这段:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateDetail(OrderViewModel createViewModel)
{
if (ModelState.IsValid)
{
createViewModel.OrderDetail.OrderHeadId = createViewModel.OrderHead.Id;
db.OrderDetails.Add(createViewModel.OrderDetail);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(createViewModel);
}
黄色部分没有值,但是我之前在Get响应时是通过ViewModel明确传入进去的,而且页面上也显示出来了,但是回传时却没了,是不是Get和Post的生命周期是不同的,2个完全独立?
@夕阳寸草畔: 我看到你的 OrderHeader是一个DisplayNameFor,一个DisplayFor,是不是Display的问题?建议试下EditorFor或者TextBoxFor这两个方法。
建议你还是初始化一下 OrderDetail 为好。
2015-12-09 14:04:50 修改:
-----------------------------------------------------------------------------
刚才我看错了,Display是你的本意。
这确实是个奇怪的问题,直觉告诉我不太好找,现在有点忙,回头帮你看看。
@滴水有灵: 感谢您的提醒!
问题解决了,结合楼上2位的意见,我重新理了下页面生存周期的概念;
在Get的Action里初始化了OrderDetail,并把如下代码放到了Get的Action里:
其实最关键的一步是在View里添加了:
TempData["Mvc"]="Mvc" TempData也可用于在一次请求中多个Action之间传递数据