Encuadernar una lista editable de niños

TL; DR: En mi aplicación ASP.NET MVC3, ¿cómo debo implementar una Vista que me permita editar detalles de una entidad 'principal' al mismo tiempo que los detalles de una lista de entidades 'secundarias'?

Actualiza: Estoy aceptando@ respuesta de torm porque él proporcionóun enlac que da alguna explicación de por qué mi solución actual puede ser tan buena como parece. @Sin embargo, nos encantaría saber si alguien más tiene alguna alternativa!

He estado buscando y leyendo (consulte la sección 'Referencias' en la parte inferior para ver algunos de los resultados hasta ahora). Sin embargo, todavía siento que hay algo 'maloliente' con las soluciones que encontré hasta ahora. Me pregunto si alguno de ustedes tiene una respuesta o sugerencia más elegante (o puede explicar por qué esto puede ser "tan bueno como parece"). @¡Gracias por adelantado

Entonces, aquí está la configuración:

Los modelos
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; }
}
El controlador
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);
    }
    /* ... */
}
The View: Vistas \ Lista de deseos \ 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>
}
La plantilla del editor: Vistas \ Compartido \ Plantillas del editor \ 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>
¿Que esta pasando

La configuración anterior genera una página con elementos de entrada estándar para el modelo de lista de deseos 'principal':

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

Para los elementos de deseo 'hijos' en la tabla, obtenemos elementos de entrada indexados:

<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" />

Esto lleva a unaWishlist wishlist argumento PUBLICADO con una @ vac.Wishitems propiedad

La firma alternativa para el controlador POST [HttpPost] public ActionResult Edit(Wishlist wishlist, ICollection<Wishitem> wishitems)) todavía me da una @ vacwishlist.Wishitems, pero me permite acceder a la @ (potencialmente modificadwishitems.

En este segundo escenario, puedo hacer algo de enlace personalizado. Por ejemplo (no es el código más elegante que he visto en mi carrera):

[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);
    }
}
Mi lista de deseo

Desearía que hubiera una forma de obtener todos los datos PUBLICADOS en un solo objeto estructurado, por ejemplo:

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

Conwishlist.Wishitems lleno de los elementos (potencialmente modificados)

O una forma más elegante para mí de manejar la fusión de los datos, si mi controlador debe recibirlos por separado. Algo com

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

¿Sugerencias, consejos, pensamientos?

Notas: Este es un ejemplo de Sandbox. La aplicación real en la que estoy trabajando es bastante diferente, no tiene nada que ver con el dominio expuesto en Sandbox. No estoy usando 'ViewModels' en el ejemplo, porque hasta ahora no parecen ser parte de la respuesta. Si son necesarios, ciertamente los presentaría (y en la aplicación real en la que estoy trabajando ya los estamos usando).e manera similar, el repositorio se abstrae mediante la clase SandboxDbContext simple en este ejemplo, pero probablemente se reemplazaría por un patrón genérico de Repositorio y Unidad de Trabajo en la aplicación real. La aplicación Sandbox se construye usando:Visual Web Developer 2010 ExpressHotfix para Microsoft Visual Web Developer 2010 Express - ENU (KB2547352)Hotfix para 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 MVC3Sintaxis de Razor para las Vistas Código de primer enfoqueEntity Framework 4.2.0.0Sandbox está diseñado para .NET Framework 4Referencias:

"Introducción a ASP.NET MVC3" Cubre los conceptos básicos, pero no trata las relaciones modelo

"Comenzando con EF usando MVC" an-asp-net-mvc-application En particularParte 6 muestra cómo lidiar con algunas de las relaciones entre los modelos. Sin embargo, este tutorial utiliza unaFormCollection argumento para su controlador POST, en lugar del enlace automático del modelo. En otras palabras: [HttpPost] Public ActionResult Edit (int id, FormCollection formCollection) En lugar de algo similar a [HttpPost] public ActionResult Edit (InstructorAndCoursesViewModel viewModel) Además, la lista de Cursos asociados con un Instructor dado está representada (en el UI) como un conjunto de casillas de verificación con el mismo nombre (que conduce a unastring[] argumento para el controlador POST), no es exactamente el mismo escenario que estoy viendo.

"Edición de una lista de longitud variable, estilo ASP.NET MVC2" Basado en MVC2 (así que me pregunto si todavía describe la mejor opción ahora que tenemos MVC3). Es cierto que (todavía) no he tenido que lidiar con las inserciones y / o la eliminación de modelos de Niños de la lista. Además, esta solución:

relies en el código personalizado (BeginCollectionItem), que está bien si es necesario (pero ¿aún es necesario en MVC3?) maneja la lista como una colección independiente, en lugar de una propiedad de un modelo de envoltura; en otras palabras, hay un modelo circundante "GiftsSet" (equivalente al modelo de lista de deseos principal en mi ejemplo), aunque no sé si la introducción de un modelo primario explícito invalida esta solución o no.

"Formato de conexión ASP.NET para la vinculación de modelos a matrices, listas, colecciones, diccionarios"a publicación de @ Scott Hanselman es una de las referencias más citadas sobre el tema de la vinculación a listas en aplicaciones MVC. Sin embargo, simplemente está describiendo las convenciones de nomenclatura adoptadas por el marco y utilizadas para generar objetos que coinciden con su método de acción (tenga en cuenta que el artículo no tiene ningún ejemplo de generar una página que luego envíe datos a una de las acciones descritas). Esta es una gran información si nosotrosTiene qu generar el HTML nosotros mismos. ¿Tenemos que hacerlo

"Modelo de enlace a una lista" Otra referencia principal, por Phil Haack. Tiene la misma información que la publicación de Hansleman anterior, pero también nos muestra que podemos usar HtmlHelpers dentro de un bucle for (int i = 0; i < 3; i++) { Html.TextBoxFor(m => m[i].Title) }), o en una plantilla de editor Html.EditorFor(m=>m[i])). Sin embargo, utilizando este enfoque, el HTML generado por la plantilla del editor no incluiría ningún prefijo específico (por ejemplo: los nombres y los identificadores de los elementos de entrada estarían en la forma[index].FieldName me gusta:[0].Quantity o[1].Name). Esto puede o no ser crítico en el ejemplo, pero probablemente será un problema en mi aplicación real, donde pueden aparecer diferentes listas 'paralelas' de niños en la misma vista.

Respuestas a la pregunta(2)

Su respuesta a la pregunta