Подтвердить что ты не робот

Использование шаблона ViewModel с помощью MVC 2 сильно типизированных HTML-помощников

Я работаю с ASP.NET MVC2 RC и не могу понять, как получить HTML-помощник, TextBoxfor для работы с шаблоном ViewModel. При использовании на странице редактирования данные не сохраняются при вызове функции UpdateModel() в контроллере. Я привел следующие примеры кода из приложения NerdDinner.

Edit.aspx

<%@ Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.DinnerFormViewModel>" %>
...
<p>
    // This works when saving in controller (MVC 1)
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBox("Title", Model.Dinner.Title) %>
    <%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
    // This does not work when saving in the controller (MVC 2)
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBoxFor(model => model.Dinner.Title) %>
    <%= Html.ValidationMessageFor(model=> model.Dinner.Title) %>
</p>

DinnerController

// POST: /Dinners/Edit/5

[HttpPost, Authorize]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

Когда используется оригинальный стиль помощника (Http.TextBox), вызов UpdateModel (ужин) работает как ожидалось, и новые значения сохраняются.

Когда используется вспомогательный стиль нового (MVC2) (Http.TextBoxFor), вызов UpdateModel (ужин) не обновляет значения. Да, текущие значения загружаются на страницу редактирования при загрузке.

Есть ли что-то еще, что мне нужно добавить в код контроллера для его работы? Новый помощник отлично работает, если я просто использую модель, а не шаблон ViewModel.

Спасибо.

4b9b3361

Ответ 1

Проблема в том, что ваша форма редактирования использует сильно типизированные помощники в отношении типа DinnerFormViewModel, но вы вызываете UpdateModel в стиле Ужин.

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

Однако это очень легко исправить. Вы можете предоставить префикс UpdateModel, который указывает, что вы не пытались отредактировать всю модель, вы пытались изменить свойство модели, в данном случае Ужин.

UpdateModel(dinner, "Dinner");

Другой подход заключается в вызове UpdateModel на фактическом ViewModel.

var viewModel = new DinnerFormViewModel();
viewModel.Dinner = repository.GetDinner(id);
UpdateModel(viewModel);

Я думаю, что первый подход намного лучше.

Ответ 2

На странице 90 в "Wrox Professional ASP.NET MVC 2" книга содержит код:

if (TryUpdateModel(dinner)) {
     dinnerRepository.Save();

     redirectToAction("Details", new { id=dinner.DinnerID });

Но он должен читать:

if (TryUpdateModel(dinner, "Dinner")) {
     dinnerRepository.Save();

     redirectToAction("Details", new { id=dinner.DinnerID });

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

Поэтому, когда модель хочет обновить свое свойство Title, он будет искать Dinner.Title, а не просто Title в поставщике значений контроллера.

Во время отладки загляните в метод Edit ActionResult и проверьте входной параметр FormCollection. Когда вы выберете в него массив записей, вы найдете "Ключи", которые начинаются с префикса объекта свойства, на который вы ссылаетесь в вашем представлении, в вашем случае "Редактировать вид", например:

<%: Html.TextBoxFor(model => model.Dinner.Title, new {size=50, @class="prettyForm" })%>

Ответ 3

Я не уверен на 100%, но кажется, что сильно типизированный помощник создает идентификаторы/имена "Dinner.Title" вместо "Title", и поэтому - UpdateModel не может связывать его.

К сожалению - я не использовал метод UpdateModel, поэтому я не знаю решения.

Не могли бы вы добавить html, который будет отображаться для обоих подходов?


Игра с рефлекторным банкоматом.

Вот что я нашел:

protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel: class
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }
    if (valueProvider == null)
    {
        throw new ArgumentNullException("valueProvider");
    }
    Predicate<string> predicate = delegate (string propertyName) {
        return BindAttribute.IsPropertyAllowed(propertyName, base.includeProperties, base.excludeProperties);
    };
    IModelBinder binder = this.Binders.GetBinder(typeof(TModel));
    ModelBindingContext context2 = new ModelBindingContext();
    context2.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(delegate {
        return base.model;
    }, typeof(TModel));
    context2.ModelName = prefix;
    context2.ModelState = this.ModelState;
    context2.PropertyFilter = predicate;
    context2.ValueProvider = valueProvider;
    ModelBindingContext bindingContext = context2;
    binder.BindModel(base.ControllerContext, bindingContext);
    return this.ModelState.IsValid;
}

Параметры
- модель Экземпляр модели для обновления.
- префикс Префикс, используемый при поиске значений в поставщике значений.


Итак - вы можете попытаться использовать перегрузку UpdateModel<T>(T model, string prefix) и передать "Ужин" или "Ужин". как префиксный аргумент.

Ответ 4

Возможно, проще всего это сделать следующим образом. Если вы вырезаете и вставляете код из загрузки wrox для учебника NerDDinner, вы обнаружите, что есть некоторые ошибки. Используя предложение сверху, я изменил пример из 1-53.txt, чтобы заставить его работать. Это изменение следует:

 //
  // POST: /Dinners/Edit/2
  [HttpPost]
  public ActionResult Edit(int id, FormCollection formValues)
  {
   // Retrieve existing dinner
   Dinner dinner = dinnerRepository.GetDinner(id);
   DinnerFormViewModel viewModel = new DinnerFormViewModel(dinner);

   if (TryUpdateModel(viewModel))
   {
    // Persist changes back to database
    dinnerRepository.Save();
    // Perform HTTP redirect to details page for the saved Dinner
    return RedirectToAction("Details", new { id = dinner.DinnerID });
   }
   else
   {
    return View(viewModel);
   }
  }

Ответ 5

Более простой способ - использовать префикс в качестве имени параметра, просто выполните следующие действия:

public ActionResult Edit(Dinner Dinner, int DinnerID)
{
   ...
}