ASP.NET MVC 3 собирает / передает данные из представлений с вложенными коллекциями — данные теряются в процессе

#c# #asp.net-mvc-3

#c# #asp.net-mvc-3

Вопрос:

Моя модель представляет собой простое родительское / дочернее представление, выглядящее следующим образом

 public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Child> Children { get; set; } 
}

public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }
}
  

Теперь я хочу создать страницу, которая позволит мне редактировать / создавать дочерние элементы — по одному дочернему элементу за раз просто отлично. Я хочу передать родительский элемент в представление, создать текстовое поле для дочернего элемента.Назовите, соберите все, что вводят пользователи, а затем сохраните это в базе данных.

Итак, в первом действии CreateParent в моем контроллере я создаю родительский элемент (с переданным идентификатором, поскольку мне нужно отслеживать этот идентификатор [в конце концов, мне нужно знать, для какого родительского элемента мне нужно добавить дочерний элемент]) выглядит так:

 [HttpGet]
public ActionResult CreateChild(int id)
{
    var newParent = new Parent { 
        Id = id, Children = new List<Child> { new Child { Name = "ChildName" } } };
    return View("~/Views/Home/CreateParent.cshtml", newParent);
}
  

Страница загружена, пользователь заполняет детали и нажимает «ОК». Затем выполняется это действие:

 [HttpPost]
public ActionResult CreateChild(Parent parent)
{
      return View("~/Views/Home/ViewChildren.cshtml", AddChild(parent));
}
  

addChild(parent) — это частная функция, которая обрабатывает сохраняющиеся дочерние элементы для данного родителя и возвращает родительскую модель с обновленными дочерними элементами.

Вот моя проблема / вопрос: данные теряются между вызовами. Когда я создаю родительский элемент в [HttpGet], все в порядке, я вижу значения, правильно установленные в отладчике, но когда я заполняю дочерние данные и нажимаю «OK» в [HttpPost], родительский элемент получает все значения, включая мой недавно заполненный дочерний элемент как null (на самом деле переносится только поле Id is, которое я также не понимаю). Почему?

Я могу добавить, что если я сделаю это с представлением, которое введено для дочернего элемента, тогда это сработает. На самом деле я не могу просто передать дочерний элемент, потому что мне также нужно отслеживать идентификатор родителя / переноса. Я тоже этого не понимаю.

Вот представления (первое — для просмотра дочерних элементов под родительским), и это работает нормально:

 @model Parent

@{
    ViewBag.Title = "AddChild";
}
<h2>Children for parent @Model.Name </h2>
<p>
    @Html.ActionLink("Create New", "CreateChild", new {id = @Model.Id})
</p>

<table>
    <tr>
        <th>
            Name
        </th>
    <tr>
@foreach (var child in @Model.Children) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => child.Name)
        </td>
        <td>
            @Html.ActionLink("Delete", "Delete", new { id=child.Id })
        </td>
    </tr>
}
</table>
  

Вот представление для создания дочернего элемента под родительским:

 @model Parent
@{
    ViewBag.Title = "CreateChild";
}

<h2>Create new child for @Model.Name</h2>

@using (Html.BeginForm())
{ 
    <table>
    <tr>
        <td>
            Name:
        </td>
    </tr>

@foreach (var child in @Model.Children)
{
    <tr>
        <td>
            @Html.EditorFor(modelItem => child.Name)
        </td>
    </tr>
}
</table>
<input id="submit" name="submit" type="submit" value="Save" />
}
  

Поэтому, когда я нажимаю «Сохранить», родитель приходит только с идентификатором, установленным на значение, которое было передано. Все остальное имеет значение null. Включая имя родительского элемента, а также дочерних элементов.

Вот метод addChild:

 private Parent AddChild(Parent parent)
{
   var updatedParent = GetParent(int parent.Id);
   updatedParent.Children.Add(parent.Children[0]); // this obviously fails as Children is NULL
   return updatedParent;
}
  

Комментарии:

1. Не могли бы вы опубликовать свой код представления и AddChild пожалуйста?

Ответ №1:

Parent.Name равно нулю, потому что вы его не публикуете. Вам нужно будет добавить @Html.TextBoxFor() или @Html.HiddenFor() . Но вам это не нужно, потому что вы загружаете сохраненный родительский элемент.

Вместо этого попробуйте for цикл для дочерних элементов.

 @for (var n = 0; n < Model.Children.Count; n  )
{
    <tr>
        <td>
            @Html.EditorFor(modelItem => Model.Children[n].Name)
        </td>
    </tr>
}
  

Это связано с тем, как ModelBinder работает. Знайте это хорошо, ModelBinder это ваш друг ;).

Редактировать: прежде чем я получил все мило в конце, я хотел упомянуть — обратите внимание на то, как помощник отображает name атрибут inputs с помощью for цикла (в отличие от foreach цикла), чтобы понять, что ModelBinder ищет.