绑定可编辑的子项列表

时间:2011-12-20 03:19:10

标签: asp.net-mvc-3 model-binding

TL; DR :在我的ASP.NET MVC3应用程序中,我应该如何实现一个允许我编辑父母'的详细信息的视图。实体同时作为“孩子”列表的详细信息。实体?

更新 :我接受了@torm's answer,因为他提供了a link,说明了为什么我目前解决方案可能会得到很好的解决方案。 但是,我们很乐意听到其他人有其他选择!

我一直在搜索和阅读(到目前为止的一些调查结果,请参阅底部的'参考文献'部分)。 但是,我仍然觉得有什么东西可以闻到'臭到目前为止我找到的解决方案。我想知道你们中是否有人有更优雅的答案或建议(或者可以解释为什么这可能是“和它一样好”#39;)。 提前致谢!

所以,这是设置:

模特:

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);
    }
    /* ... */
}

视图:视图\ 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>
}

编辑器模板:Views \ Shared \ EditorTemplates \ 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" />  

对于孩子们来说&#39;表中的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);
    }
}

我的心愿单

我希望有一种方法可以将所有POSTed数据放在一个结构化对象中,例如:

[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中公开的域无关。
  • 我没有使用&#39; ViewModels&#39;在这个例子中,因为 - 到目前为止 - 他们似乎不是答案的一部分。如果它们是必要的,我肯定会介绍它们(在真正的应用程序中,我们已经在使用它们了。)
  • 同样,在此示例中,存储库由简单的SandboxDbContext类抽象,但可能会被真实应用程序中的通用存储库和工作单元模式所取代。
  • 使用以下方法构建Sandbox应用:
    • Visual Web Developer 2010 Express
      • Microsoft Visual Web Developer 2010 Express的修补程序 - ENU(KB2547352)
      • Microsoft Visual Web Developer 2010 Express的修补程序 - ENU(KB2548139)
      • Microsoft Visual Web Developer 2010 Express - ENU Service Pack 1(KB983509)
    • .NET Framework 4.0.30319 SP1Rel
    • ASP.NET MVC3
      • 视图的Razor语法
      • 代码优先方法
    • Entity Framework 4.2.0.0
  • Sandbox是针对.NET Framework 4构建的

的参考文献:

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

  • "Getting Started with EF using MVC" AN-ASP净MVC应用程序 特别是Part 6显示了如何处理模型之间的一些关系。 但是,本教程对其POST处理程序使用FormCollection参数,而不是自动模型绑定。 换一种说法: [HttpPost] public ActionResult Edit(int id,FormCollection formCollection) 而不是某些东西 [HttpPost] public ActionResult Edit(InstructorAndCoursesViewModel viewModel) 此外,与给定教师关联的课程列表(在UI中)表示为一组具有相同名称的复选框(导致POST处理程序的string[]参数),与我不完全相同的情况我在看。

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

    • 依赖于自定义代码(BeginCollectionItem) - 如果有必要,这很好(但在MVC3中是否仍然需要?)
    • 将列表作为独立的集合处理,而不是包装模型的属性 - 换句话说,周围有&#34; GiftsSet&#34; model(相当于我的示例中的父Wishlist模型),虽然我不知道引入显式父模型是否会使此解决方案无效。
  • "ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries" Scott Hanselman的帖子是MVC应用程序中绑定到列表主题的最引用的参考儿子之一。 但是,他只是简单地描述框架采用的命名约定,并用于生成与您的操作方法匹配的对象(请注意文章没有生成页面的示例,然后将数据提交到所描述的操作之一)。 如果我们必须自己生成HTML,这是很好的信息。我们必须吗?

  • "Model Binding To A List" Phil Haack的另一个热门参考。它有一些与上面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}})。这在示例中可能或可能不是关键,但在我的实际应用中可能是一个问题,其中不同的并行&#39;儿童名单可能出现在同一视图中。

1 个答案:

答案 0 :(得分:1)

您可能需要查看此链接http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx我遇到了类似的问题,上面的内容让我理解了它

相关问题