Вставка нескольких сущностей в таблицу связи «многие ко многим»

Мое веб-приложение имеет связь «многие ко многим» между объектами Station и Timetable, а объект, который их связывает, называется StationTimetable, который также содержит данные о связи между двумя объектами. Когда я создаю новый Timetable, я хочу создать новые (и несколько) записей StationTimetable в базе данных, чтобы они соответствовали этому новому Timetable.

До сих пор мне удалось заставить приложение сохранять только одну запись StationTimetable, когда пользователь создает новое расписание. Я как бы понимаю, почему это так, и я попытался изменить его, чтобы сохранить несколько записей, однако теперь я упираюсь в кирпичную стену, насколько мне известно.

Итак, у меня есть TimetablesCreateViewModel, модель, которую использует мое представление:

public class TimetablesCreateViewModel
{
    public Timetable Timetable { get; set; }

    public IEnumerable<StationTimetable> StationTimetables { get; set; }

    public Station Station { get; set; }

    public SelectList StationList { get; set; }
}

На мой взгляд, я позволяю пользователю вставлять до 10 StationTimetable записей:

@for (var i = 0; i < 10; i++)
{
    <tr>
        <td>@Html.DropDownListFor(model => model.StationTimetables.StationId, Model.StationList, "---", htmlAttributes: new { @class = "form-control col-md-2" })</td>
        <td>@Html.EditorFor(model => model.StationTimetables.Arrival, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.EditorFor(model => model.StationTimetables.Departure, new { htmlAttributes = new { @class = "form-control" } })</td>
    </tr>
}

И затем в моем методе контроллера я хочу сделать что-то вроде следующего:

public ActionResult Create(TimetablesCreateViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        db.Timetables.Add(viewModel.Timetable);

        // Somehow loop over viewModel.StationTimetables and add them here?
        // db.StationTimetables.Add(viewModel.StationTimetable);

        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(viewModel);
}

Непосредственная проблема заключается в том, что методы DropDownListFor и EditorFor в моем представлении жалуются, и это имеет смысл, поскольку я указал StationTimetables в моей модели представления как IEnumerable, и я не могу получить доступ к свойствам StationId, Arrival и Departure IEnumerable. Эти свойства являются частью сущности, которой владеет IEnumerable.

К сожалению, я не знаю, как использовать помощник Html для создания формы, в которой я могу заполнить StationTimetables рядом StationTimetable сущностей. Если бы я мог просто получить несколько записей StationTimetable в свой метод Create, я не думаю, что после этого у меня возникнут проблемы с добавлением их в базу данных.

Спасибо за любую помощь.


person John Dorean    schedule 20.01.2015    source источник
comment
Существуют ли другие свойства навигации в классах Timetable и Station? А что касается жалоб на помощники Html, возможно, я бы использовал StationTimetables.ToList() перед доступом к свойствам.   -  person Ghukas    schedule 20.01.2015


Ответы (1)


Вам нужно сделать StationTimetables свойство IList и использовать индексатор, чтобы свойства были правильно названы и могли быть привязаны к обратной публикации.

@for (var i = 0; i < 10; i++)
{
  <tr>
    <td>@Html.DropDownListFor(m => m.StationTimetables[i].StationId, Model.StationList, "---", htmlAttributes: new { @class = "form-control col-md-2" })</td>
    <td>@Html.EditorFor(m => m.StationTimetables[i].Arrival, new { htmlAttributes = new { @class = "form-control" } })</td>
    <td>@Html.EditorFor(m => m.StationTimetables[i].Departure, new { htmlAttributes = new { @class = "form-control" } })</td>
  </tr>
}

Это создаст правильные атрибуты имени, например

<input name="StationTimetables[0].StationId" ...>
<input name="StationTimetables[1].StationId" ...>

Если вы не можете изменить его с IEnumerable, вы можете создать собственный EditorTemplate для typeof StationTimetable

Представления/Общие/EditorTemplates/StationTimetable.cshtml

@model YourAssembly.StationTimetable
<tr>
  <td>@Html.DropDownListFor(m => m.StationId, (SelectList)ViewData["StationList"], "---", new { @class = "form-control col-md-2" })</td>
  <td>@Html.EditorFor(m => m.Arrival, new { htmlAttributes = new { @class = "form-control" } })</td>
  <td>@Html.EditorFor(m => m.Departure, new { htmlAttributes = new { @class = "form-control" } })</td>
</tr>

а затем в основном виде

@EditorFor(m => m.StationTimetables, new { StationList = Model.Model.StationList })

Этот подход означает, что вам нужно инициализировать свойство StationTimetables с 10 элементами в контроллере, прежде чем передавать модель в представление.

В любом случае, я рекомендую подход EditorFor() в вашем случае из-за ошибки (сообщенной, но еще не исправленной) при использовании DropDownListFor() в цикле for.

Однако я подозреваю, что у вас не всегда есть ровно 10 расписаний станций (я позволяю пользователю вставлять до до 10), что делает этот подход негибким — например, если вы добавьте атрибуты проверки к свойствам, модель будет недействительной, если все 10 свойств расписания не будут заполнены. Было бы лучше использовать javascript или BeginCollectionItem для динамического добавления нового расписания. Пример обоих подходов включен в этот ответ

person Community    schedule 22.01.2015