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
- Visual Web Developer 2010 Express
- Песочница встроен таргетинг.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
). Это может быть или не быть критичным в этом примере, но, вероятно, будет проблемой в моем реальном приложении, где в этом представлении могут появляться разные «параллельные» списки детей.
вам необходимо сформулировать свой вопрос, который может быть легко читаемыми – user1006544