2011-12-20 9 views
10

TL; DR: Nella mia app ASP.NET MVC3, come devo implementare una vista che mi consente di modificare i dettagli di un'entità "padre" contemporaneamente ai dettagli di un elenco delle entità "bambini"?Associazione di un elenco modificabile di bambini

Aggiornamento: Sono accettare @torm's answer perché ha fornito a link che dà qualche spiegazione sul perché la mia soluzione attuale può essere buono come si arriva. Tuttavia, ci piacerebbe sapere se qualcun altro ha qualche alternativa!

Sono stato a cercare e leggere (vedere la sezione "Riferimenti" in fondo per alcuni dei risultati finora). Tuttavia, mi sembra ancora che ci sia qualcosa di "puzzolente" con le soluzioni che ho trovato finora. Mi chiedo se qualcuno di voi ha una risposta o un suggerimento più elegante (o può spiegare perché questo potrebbe essere "buono come si arriva"). Grazie in anticipo!

Quindi, ecco il programma di installazione:

I modelli:

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; } 
} 

Il controllore:

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: Vista \ 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> 
} 

Il modello di editor: 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> 

Cosa sta succedendo?

La configurazione sopra genera una pagina con gli elementi di ingresso standard per il 'genitore' Wishlist modello:

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

Per Wishitems i 'bambini' nella tabella, otteniamo elementi di input indicizzati:

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

Questo porta a un argomento Wishlist wishlist POST indietro con una proprietà .Wishitems vuota.

La firma alternativa per il gestore POST ([HttpPost] public ActionResult Edit(Wishlist wishlist, ICollection<Wishitem> wishitems)) mi restituisce ancora un wishlist.Wishitems vuoto, ma mi consente di accedere a (potenzialmente modificato) wishitems.

In questo secondo scenario, posso eseguire il binding personalizzato.Per esempio (non il codice più elegante che ho visto nella mia carriera):

[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); 
    } 
} 

La mia lista

Vorrei che ci fosse un modo per me per ottenere tutti i dati pubblicati in un unico oggetto strutturato , ad esempio:

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

con wishlist.Wishitems riempita con gli articoli (potenzialmente modificati)

o un altro el modo egantesco per me di gestire la fusione dei dati, se il mio controller deve riceverli separatamente. Qualcosa di simile

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

consigli, suggerimenti, pensieri?

Note:

  • Questo è un esempio Sandbox. L'applicazione effettiva su cui sto lavorando è abbastanza diversa, non ha nulla a che fare con il dominio esposto in Sandbox.
  • Non sto usando 'ViewModels' nell'esempio, perché -so lontano- non sembrano far parte della risposta. Se sono necessari, li introdurrei certamente (e nella vera app a cui sto lavorando li stiamo già utilizzando).
  • Analogamente, il repository è astratto dalla semplice classe SandboxDbContext in questo esempio, ma probabilmente verrà sostituito da un modello generico di Repository e Unit Of Work nell'app reale.
  • L'applicazione Sandbox è costruito utilizzando:
    • Visual Web Developer 2010 Express
      • Hotfix per Microsoft Visual Web Developer 2010 Express - ITA (KB2547352)
      • Hotfix per Microsoft Visual Web Developer 2010 Express - ENU (KB2548139)
      • Microsoft Visual Web Developer 2010 Express - ITA service pack 1 (KB983509)
    • .NET Framework 4.0.30319 SP1Rel
    • ASP.NET MVC3
      • Razor sintassi per le Visualizzazioni
      • Codice-Primo approccio
    • Entity Framework 4.2.0.0
  • Sandbox è un targeting costruito.NET Framework 4

Riferimenti:

  • "Getting Started with ASP.NET MVC3" copre le basi, ma non affronta i rapporti modello

  • "Getting Started with EF using MVC" un-asp-net-MVC-applicativi In particolare Part 6 mostra come gestire alcune delle relazioni tra i modelli. Tuttavia, questo tutorial utilizza un argomento FormCollection per il relativo gestore POST anziché il binding automatico del modello. In altre parole: [HttpPost] ActionResult pubblico Edit (int id, FormCollection FormCollection) Piuttosto che qualcosa lungo le linee di [HttpPost] ActionResult pubblico Edit (InstructorAndCoursesViewModel ViewModel) Inoltre, l'elenco dei corsi associati a un determinato istruttore è rappresentato (nell'interfaccia utente) come un insieme di checkbox con lo stesso nome (che porta a un argomento string[] per il gestore POST), non proprio lo stesso scenario che sto guardando.

  • "Editing a variable length list, ASP.NET MVC2-style" Basato su MVC2 (quindi mi chiedo se è ancora descrive l'opzione migliore ora che abbiamo MVC3). Devo ammettere che non ho (ancora) avuto a che fare con gli inserimenti e/o la rimozione dei modelli di bambini dalla lista. Inoltre, questa soluzione:

    • si basa su codice personalizzato (BeginCollectionItem) - che va bene se è necessario (ma è ancora necessario nella MVC3?)
    • gestisce la lista come una collezione di free-standing, piuttosto che una proprietà di un modello di wrapping - in altre parole, c'è un modello "GiftsSet" circostante (equivalente al modello di Wishlist genitore nel mio esempio), anche se non so se l'introduzione di un modello genitore esplicito invalida o meno questa soluzione. Il post di
  • "ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries" Scott Hanselman è uno dei figlio di riferimento più citato il tema del legame alle liste nelle applicazioni MVC. Tuttavia, sta semplicemente descrivendo le convenzioni di denominazione adottate dal framework e utilizzato per generare oggetti che corrispondono al metodo di azione (notare come l'articolo non ha esempi di generazione di una pagina che quindi invia dati a una delle azioni descritte). Questa è una grande informazione se noi dobbiamo generare noi stessi il codice HTML. Dobbiamo?

  • "Model Binding To A List" Un altro riferimento top, di Phil Haack. Ha alcune delle stesse informazioni del post di Hansleman in alto, ma ci mostra anche che possiamo usare HtmlHelpers all'interno di un ciclo (for (int i = 0; i < 3; i++) { Html.TextBoxFor(m => m[i].Title) }) o in un modello di editor (Html.EditorFor(m=>m[i])). Tuttavia, utilizzando questo approccio, l'HTML generato dal modello di editor non includerebbe alcun prefisso specifico (ad esempio: i nomi e gli id ​​degli elementi di input sarebbero nel formato [index].FieldName come: [0].Quantity o [1].Name). Questo può o non può essere critico nell'esempio, ma probabilmente sarà un problema nella mia attuale applicazione, in cui diversi elenchi "paralleli" di bambini potrebbero apparire nella stessa vista.

+0

è necessario per inquadrare il problema che può essere facilmente leggibile – user1006544

risposta

1

Si consiglia di controllare questo link http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx ho avuto problema simile e quanto sopra mi ha permesso di capire che

+0

Grazie per il link. Quindi .. Devo solo mordere il proiettile e scrivere il mio raccoglitore personalizzato, dopo tutto? Bene, almeno ora so perché il binder predefinito è insufficiente e posso tenere d'occhio gli eventuali aggiornamenti da MS. Grazie per il consiglio; Aspetterò un po 'prima di accettare la risposta (voglio vedere se qualcun altro suggerisce alternative, e voglio provarlo nella mia app Sandbox a casa stasera). – FOR

+0

sì, per favore aspetta, sono anche interessato alla soluzione. Finora ho finito per iterare attraverso la raccolta del mio oggetto e inviare ciascuna sub-entità una per una usando jQuery $ .post ma ha alcune limitazioni – torm

+0

link è rotto :( – TWilly

Problemi correlati