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

Пользователь запрашивает страницу для Шага 1, заполняет и отправляет форму, содержащую выбранного человека, пока все хорошо. После проверки ModelState следующая модель представления строится правильно с использованием выбранного человека. Затем я пытаюсь перенаправить действие с помощью newVM, но при входе на шаг 2 обнаруживаю, что MVC стирает модель представления, которую пытались передать. Я подозреваю, что это связано с тем, как MVC пытается обновить и создать экземпляр на основе результатов строки запроса. Я поставлю точку останова и проверю это, но мне интересно, как можно изменить представление из сообщения обратно с переданной новой моделью представления?

public ActionResult Step1()
{
    var vm = new VMStep1();
    return View(vm);
}

[HttpPost]
public ActionResult Step1(VMStep1 vm)
{
    if (ModelState.IsValid)
    {
        var newVM = new VMStep2(vm.SelectedPerson);
        return RedirectToAction("Step2", newVM);
    }
    return View(vm);
}

public ActionResult Step2(VMStep2 vm)
{
    return View(vm);
}

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


person JWP    schedule 28.10.2014    source источник
comment
Вам нужно назначить newVM сеансу (или сохранить что-то в базе данных), а затем получить его в методе Step2 (можно также использовать TempData, но произойдет сбой, если пользователь обновит браузер)   -  person    schedule 29.10.2014
comment
Спасибо, Стивен, этого я и боялся, но мы должны следовать правилам MVC.   -  person JWP    schedule 29.10.2014
comment
Не знаете, что вы подразумеваете под должны следовать правилам MVC? Два способа создания многошагового мастера: 1. Одна форма с разделами, где каждый раздел содержит кнопки «Далее/Назад», которые подтверждают каждый раздел, затем делают следующий раздел видимым и скрывают текущий раздел, а последний раздел содержит кнопку отправки, или 2. как показано в этом ответе   -  person    schedule 30.10.2014
comment
Правила MVC таковы: если вы используете строго типизированные представления и контроллеры, которые принимают эти типы в качестве параметров, то по умолчанию MVC берет верх. Он обеспечивает вход и результаты действий на основе строго типизированных представлений. В настоящее время нет способа войти в контроллер с одной моделью представления и вернуть результат с другим типом модели представления. Итак, что я сделал, чтобы преодолеть это, заключалось в том, чтобы содержать вторую модель представления в первой и просто вызывать частичное представление на основе этой другой модели представления.   -  person JWP    schedule 04.11.2014
comment
Это не "правило"! И если VMStep2 содержит только примитивные свойства, тогда ваш код будет работать нормально (RouteValueDictionary генерируется на основе свойств вашей модели).   -  person    schedule 04.11.2014
comment
При использовании сильных типов механизм связывания MVC накладывает свои правила на всех, кто их использует. Это плохо задокументировано, но это действительно так! Ничто из того, что я сказал, не было неправдой, и я не заинтересован в использовании примитивов в том виде, в каком они есть, они примитивны для того, что я делаю.   -  person JWP    schedule 05.11.2014
comment
Мусор. Я предлагаю вам потратить некоторое время на изучение исходного кода MVC, чтобы понять тонкости привязки модели и то, как работает ModelState. И что значит, что вы не заинтересованы в использовании примитивов? Примитивные типы — это Int32, Boolean, Char и т. д., определенные в CLR, и у вас могут возникнуть проблемы с созданием модели без них. Вы приняли ответ, который работает для вас, что хорошо, но вы могли бы просто передать vm.SelectedPerson в Step2, а затем инициализировать VMStep2 в этом методе.   -  person    schedule 05.11.2014
comment
Посмотрите еще раз на мой первоначальный вопрос. Пост из шага 1 использует модель представления (не примитив). Метод действия для шага 2 также использует модель представления, а НЕ примитив. Мой вопрос не имел ничего общего с примитивами. Я предлагаю вам взглянуть на код, потому что все, что я упомянул ранее, было проверено мной в обширном тестировании того, как MVC обрабатывает модели представления. MVC имеет свои собственные внутренние правила того, как он работает с ViewModels и/или моделями. Основное правило заключается в том, что они уже являются объектами до того, как контроллер увидит их, независимо от того, что было передано.   -  person JWP    schedule 05.11.2014
comment
Аааа! Я знаю, что вы передаете модель, но эта модель содержит примитивы (или типы значений). Когда вы передаете модель с помощью RedirectToAction();, внутри создается RouteValueDictionary. Если бы эта модель содержала, скажем, только int ID и string Name, она могла бы выглядеть как ID="1", Name="ABC", и она была бы привязана правильно. Однако, если модель содержит свойства, которые являются сложными типами, она не будет работать, потому что значения маршрута могут содержать что-то вроде MyComplexObjectProperty="MyAssembly.MyComplexObject", которое не будет связываться.   -  person    schedule 05.11.2014
comment
Хорошо, спасибо за подсказки, моя следующая остановка — заглянуть в RouteValueDictionary, чтобы посмотреть, как это работает. Меня очень интересует аспект привязки модели MVC. В моем случае у меня были сложные типы, потому что я использую Viewmodels для привязки модели строгого типа для всего, что видит или возвращает пользователь. Я пришел из мира MVVM, поэтому в некотором смысле я дублирую (привязка к серверу). Кроме того, я также улавливаю флюиды, что сообщество перемещает привязку строго на сторону клиента (knockout и т. д.), а также маршрутизацию... Так что, как только у меня будет шанс, я займусь этим, начиная с Knockout. Спасибо!   -  person JWP    schedule 05.11.2014


Ответы (2)


Я не понимаю, почему вы должны звонить RedirectToAction! Что он делает это следующее: он говорит вашему браузеру перенаправить и, нравится вам это или нет, ваш браузер не понимает, как обрабатывать ваш объект - он понимает только JSON. Поэтому, если вы действительно настаиваете на использовании return RedirectToAction("Step2", newVM);, вам следует рассмотреть способ сериализации вашего объекта VMStep2 в JSON, и когда браузер запрашивает перенаправление, он будет правильно передан и создан в вашем методе действия public ActionResult Step2(VMStep2 vm)

ОДНАКО я бы использовал гораздо более простой способ --- вместо

return RedirectToAction("Step2", newVM);

я хотел бы использовать

return View("Step2", newVM);
person noobed    schedule 29.10.2014
comment
Хорошо, я думаю, вы подтвердили то, что я обнаружил, что при вводе Step2 данные теряются, потому что связыватель модели MVC использовал данные запроса (аналогично JSON) для построения ViewModel для Step2. Данные были утеряны, поскольку для VMStep2 никогда не существовало конкретных данных входящего запроса. - person JWP; 29.10.2014

Спасибо всем за отличный вклад!

Вот что я сделал...

  1. Я создал три представления MainView, Step1View, Step2View (шаги 1 и 2 были частично строго типизированными представлениями).
  2. Я создал MainViewModel, содержащий VMStep1 и VMStep2.
  3. Когда контроллер обслуживал Step1, MainViewModel только инициализировал VMStep1 и устанавливал логику состояния, чтобы указать, что MainView Step1 должен быть показан.
  4. Когда пользователь отправил обратно MainView, содержащий MainViewModel, MainViewModel знал, что делать, по ответам, предоставленным в VMStep1.
  5. VMStep2 был инициализирован при отправке обратно, и состояние было установлено, чтобы указать MainView показывать Step2. VMStep1 больше не актуален и имеет значение null.
  6. Теперь пользователь мог ответить, используя VMStep2, и все было хорошо.

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

person JWP    schedule 04.11.2014