Связывание с SelectList в MVC

Снова я столкнулся с ситуацией "Это не должно быть! *! # жесткое".

Проблема: я хочу использовать форму в MVC для создания объекта. Одним из элементов объекта является набор ограниченного выбора - идеальный кандидат для выпадающего списка.

Но если я использую SelectList в своей модели и выпадающий список в моем представлении, а затем попытаюсь отправить модель обратно в мой метод Create, я получаю сообщение об ошибке "Отсутствует исключение метода: нет конструктора без параметров для этого объекта". Исследуя исходный код MVC, кажется, что для привязки к модели Binder должен иметь возможность создать ее сначала и не может создать SelectList, потому что для нее нет конструктора по умолчанию.

Здесь упрощенный код: Для модели:

public class DemoCreateViewModel
{
    public SelectList Choice { get; set; }
}

Для контроллера:

//
// GET: /Demo/Create

public ActionResult Create()
{
    DemoCreateViewModel data = new DemoCreateViewModel();
    data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
    ViewData.Model = data;
    return View();
}

//
// POST: /Demo/Create

[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
    try
    {
        // TODO: Add insert logic here

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

И для представления:

<fieldset>
    <legend>Fields</legend>
    <%= Html.LabelFor(model => model.Choice) %>
    <%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

Теперь я знаю, что могу сделать эту работу, отбросив назад 10 ярдов и набрав: привязку модели обхода и возврат к FormCollection, а также проверку и привязку всех полей самостоятельно, но это будет более простой способ. Я имею в виду, это примерно так же просто, как и нужно. Есть ли способ сделать эту работу в архитектуре MVC ModelBinding? Если так, то, что это? А если нет, то как?

Edit: Ну, у меня есть яйцо на моем лице, но, возможно, это поможет кому-то другому. Я сделал еще несколько экспериментов и нашел простое решение, которое, похоже, работает.

Укажите простое значение (строка или целое число, в зависимости от того, какой тип значения выбранного списка), и укажите имя как элемент модели, к которому вы привязываетесь. Затем укажите второй элемент в качестве списка выбора вариантов и назовите его чем-то другим. Итак, моя модель стала:

public class DemoCreateViewModel
{
    public string Choice { get; set; }
    public SelectList Choices { get; set; }
}

И затем оператор DropDownListFor в представлении становится:

<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>

Когда я это делаю, кнопка submit правильно привязывает выбор, сделанный в форме к строке Choice, и возвращает модель ко второму методу Create.

Ответ 1

Вот один из подходов:

@Html.DropDownListFor(model => model.Choice, 
                      ViewBag.Choices as SelectList, 
                      "-- Select an option--",
                      new { @class = "editor-textbox" })

Обратите внимание, что я использую ViewBag для размещения моего SelectList. Таким образом, когда вы отправляете сообщение назад, клиент не отправляет весь список выбора до сервера как часть модели.

В вашем коде контроллера вам просто нужно установить сумку просмотра:

ViewBag.Choices = new SelectList(....

Ответ 2

Рассмотрите возможность создания другой модели представления для вашего действия post без свойства SelectList:

            public class DemoCreateViewModelForUpdate
            {
                public string Choice { get; set; }
            }

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

По вашему мнению, вы бы сделали:

            @Html.DropDownListFor(m => m.Choice, Model.Choices)

как и в предыдущем ответе, поэтому никакие ненужные данные не будут проходить в оба конца.