2011-12-20 74 views
10

TL; DR:在我的ASP.NET MVC3应用程序中,应该如何实现一个视图,该视图允许我同时编辑“父”实体的详细信息“儿童”实体列表?绑定儿童的可编辑列表

更新:我接受@torm's answer,因为他提供a link给出一些解释,为什么我目前的解决方案可能是因为它得到不错的。 但是,我们很乐意听取其他人是否有其他选择!

我一直在寻找和阅读(到目前为止,参见底部的'参考'部分底部的一些发现)。 不过,我仍然觉得迄今为止我找到的解决方案有点“臭”。我想知道你们中的任何一个人是否有更优雅的回答或建议(或者可以解释为什么这可能“尽可能好”)。 提前致谢!

所以,这里的设置:

型号:

public class Wishlist 
{ 
    public Wishlist() { Wishitems = new List<Wishitem>(); } 

    public long WishListId { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 

    public virtual ICollection<Wishitem> Wishitems { get; set; } 
} 
public class Wishitem 
{ 
    public long WishitemId { get; set; } 
    public string Name { get; set; } 
    public int Quantity { get; set; } 
} 

控制器:

public class WishlistsController : Controller 
{ 
    private SandboxDbContext db = new SandboxDbContext(); 
    /* ... */ 
    public ActionResult Edit(long id) 
    { 
     Wishlist wishlist = db.Wishlists.Find(id); 
     return View(wishlist); 
    } 

    [HttpPost] 
    public ActionResult Edit(Wishlist wishlist) 
    //OR (see below): Edit(Wishlist wishlist, ICollection<Wishitem> wishitems) 
    { 
     if (ModelState.IsValid) 
     { 
      db.Entry(wishlist).State = EntityState.Modified; 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 
     return View(wishlist); 
    } 
    /* ... */ 
} 

观:浏览次数\收藏\ Edit.cshtml

@model Sandbox.Models.Wishlist 
<h2>Edit</h2> 
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> 
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> 
@using (Html.BeginForm()) 
{ 
    @Html.ValidationSummary(true) 
    <fieldset> 
     <legend>Wishlist</legend> 
     @Html.HiddenFor(model => model.WishListId) 
     <div class="editor-label">@Html.LabelFor(model => model.Name)</div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Name) 
      @Html.ValidationMessageFor(model => model.Name) 
     </div> 
    </fieldset> 
    <table> 
     <tr> 
      <th> 
       Quantity 
      </th> 
      <th> 
       Name 
      </th> 
     </tr> 
     @for (var itemIndex = 0; itemIndex < Model.Wishitems.Count; itemIndex++) 
    { 
      @Html.EditorFor(item => Model.Wishitems.ToList()[itemIndex]) 
    } 
    </table> 
    <p> 
     <input type="submit" value="Save" /> 
    </p> 
} 

编辑器模板:视图\共享\编辑器模板\ Wishitem.cshtml

@model Sandbox.Models.Wishitem 
<tr> 
    <td> 
     @Html.HiddenFor(item=>item.WishitemId) 
     @Html.TextBoxFor(item => item.Quantity) 
     @Html.ValidationMessageFor(item => item.Quantity) 
    </td> 
    <td> 
     @Html.TextBoxFor(item => item.Name) 
     @Html.ValidationMessageFor(item => item.Name) 
    </td> 
</tr> 

这是怎么回事?

上面的设置生成标准输入元素的网页为“父”的收藏模式:

<input class="text-box single-line" id="Name" name="Name" type="text" value="MyWishlist" /> 

对于“孩子”在表中Wishitems,我们获得索引输入元素:

<input data-val="true" data-val-number="The field Quantity must be a number." data-val-required="The Quantity field is required." name="[0].Quantity" type="text" value="42" /> 
<input name="[0].Name" type="text" value="Unicorns" /> 

这会导致Wishlist wishlist参数以空的.Wishitems属性回传。

POST处理程序([HttpPost] public ActionResult Edit(Wishlist wishlist, ICollection<Wishitem> wishitems))的另一个签名仍为空wishlist.Wishitems,但允许我访问(可能已修改的)wishitems

在这第二种情况下,我可以做一些自定义绑定。例如(不是最优雅的代码我已经看到了我的职业生涯):

[HttpPost] 
public ActionResult Edit(Wishlist editedList, ICollection<Wishitem> editedItems) 
{ 
    var wishlist = db.Wishlists.Find(editedList.WishListId); 
    if (wishlist == null) { return HttpNotFound(); } 

    if (ModelState.IsValid) 
    { 
     UpdateModel(wishlist); 

     foreach (var editedItem in editedItems) 
     { 
      var wishitem = wishlist.Wishitems.Where(wi => wi.WishitemId == editedItem.WishitemId).Single(); 
      if (wishitem != null) 
      { 
       wishitem.Name = editedItem.Name; 
       wishitem.Quantity = editedItem.Quantity; 
      } 
     } 
     db.SaveChanges(); 
     return View(wishlist); 
    } 
    else 
    { 
     editedList.Wishitems = editedItems; 
     return View(editedList); 
    } 
} 

我的收藏

我希望能有让我得到在一个单一的结构化对象的所有已发布数据的方式如:

[HttpPost] 
public ActionResult Edit(Wishlist wishlist) { /* ...Save the wishlist... */ } 

随着wishlist.Wishitems充满了(可能已修改)项目

还是比较埃尔例如,如果我的控制器必须单独接收它们,我可以处理数据的合并。喜欢的东西

[HttpPost] 
public ActionResult Edit(Wishlist editedList, ICollection<Wishitem> editedItems) 
{ 
    var wishlist = db.Wishlists.Find(editedList.WishListId); 
    if (wishlist == null) { return HttpNotFound(); } 

    if (ModelState.IsValid) 
    { 
     UpdateModel(wishlist); 
     /* and now wishlist.Wishitems has been updated with the data from the Form (aka: editedItems) */ 
     db.SaveChanges(); 
     return View(wishlist); 
    } 
    /* ...Etc etc... */ 
} 

提示,技巧,想法?

注:

  • 这是一个沙盒的例子。我正在处理的实际应用程序非常不同,与在Sandbox中公开的域无关。
  • 我没有在示例中使用'ViewModels',因为 - 远 - 他们似乎不是答案的一部分。如果他们有必要,我肯定会介绍他们(并且在我正在研究的真实应用程序中,我们已经在使用它们)。
  • 同样,在本例中,存储库被简单的SandboxDbContext类抽象出来,但可能会被实际应用程序中的通用存储库和工作单元模式所取代。
  • 沙盒应用程序是使用内置:
    • 的Visual Web Developer 2010速成
      • 修复微软的Visual Web Developer 2010速成 - ENU(KB2547352)
      • 修复微软的Visual Web Developer 2010速成 - ENU(KB2548139)
      • 微软的Visual Web Developer 2010速成 - ENU的Service Pack 1(KB983509)
    • 的.NET Framework 4.0.30319 SP1Rel
    • ASP.NET MVC3
      • 剃刀语法观
      • 代码优先方法
    • 实体框架4.2.0.0
  • 沙盒构建目标。NET Framework 4的

参考文献:

  • "Getting Started with ASP.NET MVC3" 涵盖了基础知识,但不与模型关系

  • "Getting Started with EF using MVC" 的-ASP净MVC应用程序 特别处理Part 6显示了如何处理模型之间的一些关系。 但是,本教程对其POST处理程序使用FormCollection参数,而不是自动模型绑定。 换句话说: [HttpPost]公开的ActionResult编辑(INT ID,的FormCollection的FormCollection) 而不是沿着 [HttpPost]公开的ActionResult编辑(InstructorAndCoursesViewModel视图模型)的线的东西 此外,对于一个给定的讲师相关课程列表(在UI中)被表示为一组具有相同名称的复选框(导致POST处理程序的参数为string[]),这与我所看到的并不完全相同。

  • "Editing a variable length list, ASP.NET MVC2-style" 基于MVC2(所以我想知道它是否仍然描述我们有MVC3的最佳选择)。 不可否认,我还没有处理从列表中插入和/或删除儿童模型。 此外,该解决方案:

    • 依赖于自定义代码(BeginCollectionItem) - 这是,如果它是必要的罚款(但它仍然是必要的,MVC3?)
    • 处理列表作为一个独立的采集,而不是包装模型的属性 - 换言之,周围有“GiftsSet”模型(相当于我的示例中的父母Wishlist模型),尽管我不知道引入明确的父模型是否使此解决方案失效。
  • "ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries" 斯科特Hanselman的职位是被引用最多的基准儿子一个在MVC应用程序绑定到列表中的主题。但是,他只是简单地描述了框架采用的命名约定,并用于生成与您的操作方法匹配的对象(请注意文章没有生成页面然后将数据提交到所描述操作之一的示例)。 这是很好的信息,如果我们必须自己生成HTML。我们必须吗?

  • "Model Binding To A List" 菲尔哈克的另一个顶级参考。它具有与上述Hansleman帖子相同的信息,但也向我们展示了我们可以在循环(for (int i = 0; i < 3; i++) { Html.TextBoxFor(m => m[i].Title) })或编辑器模板(Html.EditorFor(m=>m[i]))中使用HtmlHelpers。 但是,使用这种方法,由编辑器模板生成的HTML不会包含任何特定的前缀(例如:输入元素的名称和ID将采用[index].FieldName的形式,如:[0].Quantity[1].Name)。在这个例子中,这可能是也可能不是关键,但在我的实际应用程序中可能会出现问题,其中不同的“平行”子项目列表可能出现在同一个视图中。

+0

需要将frame你的问题,可以轻松读取 – user1006544 2011-12-20 07:33:20

回答

1

你可能要检查这个链接http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx我有类似的问题,上面让我明白

+0

感谢您的链接。所以..我只需要咬下子弹并写下我自己的自定义联编程序?那么,至少现在我知道为什么默认联编程序是不够的,我可以留意来自MS的任何更新。谢谢你的提示;在接受答案之前,我会稍微等一下(希望看看是否有其他人建议替代方案,并且我想在今晚在家中的沙箱应用中试用它)。 – FOR 2011-12-20 13:16:32

+0

是的,请等待,我也对解决方案感兴趣。到目前为止,我已经迭代了我的对象的集合,并使用jQuery $ .post逐个提交每个子实体,但它有一些限制 – torm 2011-12-20 13:46:32

+0

链接已损坏:( – TWilly 2016-07-26 14:42:16