2011-12-20 2 views
10

TL; DR: В моем приложении ASP.NET MVC3, как мне реализовать представление, которое позволяет мне редактировать детали родительского объекта одновременно с деталями список «дочерних» организаций?Привязка редактируемого списка детей

Update: Я принимая @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); 
    } 
    /* ... */ 
} 

The View: Просмотры \ \ 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" /> 

для «детей» 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 заполнен (потенциально модифицированных) элементов

Или более эль egay способ для меня обрабатывать слияние данных, если мой контроллер должен получать их отдельно. Что-то вроде

[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. Фактическое приложение, над которым я работаю, совсем другое, не имеет ничего общего с доменом, открытым в Sandbox.
  • Я не использую «ViewModels» в этом примере, потому что, далеко, они не являются частью ответа. Если они понадобятся, я бы обязательно их представил (и в реальном приложении, над которым я работаю, мы уже используем их).
  • Аналогично, репозиторий абстрагируется простым классом SandboxDbContext в этом примере, но, вероятно, будет заменен общим шаблоном Repository и Unit Of Work в реальном приложении.
  • приложение Песочница построен с использованием:
    • Visual Web Developer 2010 Express
      • Исправление для Microsoft Visual Web Developer 2010 Express - Гумилева (KB2547352)
      • исправлений для Microsoft Visual Web Developer 2010 Express, - Гумилева (KB2548139)
      • Microsoft Visual Web Developer 2010 Express - Гумилева Service Pack 1 (KB983509)
    • .NET Framework 4.0.30319 SP1Rel
    • ASP.NET MVC3
      • Razor синтаксис соображениях
      • Code-Первый подход
    • Entity Framework 4.2.0.0
  • Песочница встроен таргетинг.NET Framework 4

Ссылки:

  • "Getting Started with ASP.NET MVC3" Охватывает основы, но не касается модели отношений

  • "Getting Started with EF using MVC" ан-Asp-сетчатой ​​MVC-приложений В частности, Part 6 показывает, как бороться с некоторыми из отношений между моделями. Однако этот учебник использует аргумент FormCollection для своего обработчика POST, а не для автоматической привязки модели. Другими словами: [HttpPost] публичное ActionResult Edit (INT идентификатор, FormCollection FormCollection) Вместо того, чтобы что-то вдоль линий [HttpPost] общественного ActionResult Edit (InstructorAndCoursesViewModel ViewModel) Кроме того, список курсов, связанных с данным инструктором (в пользовательском интерфейсе) представляет собой набор флажков с тем же именем (приводящий к аргументу string[] для обработчика POST), не совсем тот же сценарий, который я рассматриваю.

  • "Editing a variable length list, ASP.NET MVC2-style" Основано на MVC2 (так что мне интересно, если он все еще описывает лучший вариант теперь, когда у нас есть MVC3). По общему признанию, мне еще не удалось разобраться с вставками и/или удалением моделей Children из списка. Кроме того, это решение:

    • полагается на пользовательском коде (BeginCollectionItem) - это хорошо, если это необходимо (но это по-прежнему необходимо в MVC3?)
    • обрабатывает список как свободно стоящие коллекции, а не свойство модели обертывания - другими словами, существует окружающая модель «GiftsSet» (эквивалентная родительской модели Wishlist в моем примере), хотя я не знаю, делает ли явная родительская модель недействительным это решение или нет. после
  • "ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries" Скотт Хансельман является одним из самых цитируемых эталонным сына тема привязки к спискам в приложениях MVC. Однако он просто описывает соглашения об именах, принятые каркасом, и используется для создания объектов, соответствующих вашему методу действий (обратите внимание, как в статье нет примера создания страницы, которая затем передает данные одному из описанных действий). Это замечательная информация, если мы должны генерировать HTML самостоятельно. Должны ли мы?

  • "Model Binding To A List" Еще одна ссылка, Фил Хаак. Он содержит некоторую информацию, такую ​​как сообщение Hansleman выше, но также показывает, что мы можем использовать HtmlHelpers в цикле (for (int i = 0; i < 3; i++) { Html.TextBoxFor(m => m[i].Title) }) или в шаблоне редактора (Html.EditorFor(m=>m[i])). Однако, используя этот подход, HTML, сгенерированный шаблоном редактора, не будет содержать какого-либо конкретного префикса (например: имена и идентификаторы входных элементов будут иметь вид [index].FieldName: [0].Quantity или [1].Name). Это может быть или не быть критичным в этом примере, но, вероятно, будет проблемой в моем реальном приложении, где в этом представлении могут появляться разные «параллельные» списки детей.

+0

вам необходимо сформулировать свой вопрос, который может быть легко читаемыми – user1006544

ответ

1

Вы можете проверить эту ссылку http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx у меня была аналогичная проблема, и выше позволило мне понять

+0

Спасибо за ссылку. Так что .. Мне просто нужно укусить пулю и написать собственное собственное связующее? Ну, по крайней мере, теперь я знаю, почему связующее по умолчанию недостаточно, и я могу следить за любыми обновлениями от MS. Спасибо за совет; Я немного подожду, прежде чем принимать ответ (хочу узнать, предлагает ли кто-либо другие альтернативы, и я хочу попробовать это в своем приложении Sandbox дома сегодня). – FOR

+0

да, пожалуйста, подождите, меня тоже интересует решение. До сих пор я заканчивал итерирование коллекции моего объекта и подавал каждый субобъект один за другим с использованием jQuery $ .post, но у него есть некоторые ограничения. – torm

+0

ссылка не работает :( – TWilly

Смежные вопросы