Нужна помощь в использовании DefaultModelBinder для вложенной модели - PullRequest
3 голосов
/ 02 апреля 2010

Есть несколько связанных вопросов, но я не могу найти ответ, который работает.

Предполагается, что у меня есть следующие модели:

public class EditorViewModel
{
  public Account Account {get;set;}
  public string SomeSimpleStuff {get;set;}
}

public class Account
{
  public string AccountName {get;set;}
  public int MorePrimitivesFollow {get;set;}
}

и представление, расширяющее ViewPage<EditorViewModel>, которое выполняет следующее:

<%= Html.TextBoxFor(model => model.Account.AccountName)%>
<%= Html.ValidationMessageFor(model => model.Account.AccountName)%>
<%= Html.TextBoxFor(model => model.SomeSimpleStuff )%>
<%= Html.ValidationMessageFor(model => model.SomeSimpleStuff )%>

и мой контроллер выглядит так:

[HttpPost]
public virtual ActionResult Edit(EditorViewModel account)
{ /*...*/ }

Как я могу заставить DefaultModelBinder правильно связать мою EditorViewModel? Не делая ничего особенного, я получаю пустой экземпляр моего EditorViewModel со всеми значениями null или по умолчанию.

Самое близкое, что я получил, это позвонив UpdateModel вручную:

[HttpPost]
public virtual ActionResult Edit(EditorViewModel account)
{
    account.Account = new Account();
    UpdateModel(account.Account, "Account");
    // this kills me:
    UpdateModel(account);

Это успешно обновляет модель свойств моей учетной записи, но когда я вызываю UpdateModel на account (чтобы получить остальные открытые свойства моей EditorViewModel), я получаю совершенно бесполезное «Модель типа ... не может быть обновлена» «. Внутренних исключений нет, поэтому я не могу понять, что происходит.

Что мне с этим делать?

1 Ответ

4 голосов
/ 02 апреля 2010

Связыватель запутывается, потому что он видит, что параметр вашего метода действия называется account , и он видит входящие поля формы с именем account.accountname , поэтому он ищет Свойство AccountName в EditorViewModel.

Это можно исправить, переименовав параметр во что-то другое, не конфликтующее с полем входящей формы, или вы можете прикрепить атрибут [Bind (Prefix = "")] к параметру. Этот атрибут говорит: «игнорируйте тот факт, что параметр называется account , и сделайте вид, что вместо этого я дал ему пустое строковое имя». Тогда связыватель будет искать account.accountname вместо account.account.accountname .

Редактировать - дополнительная информация:

Когда связыватель видит комплексный параметр с именем foo , он просматривает текущий запрос на что-либо с именем * foo. **. Поэтому, если ваш параметр был назван foo , а его тип имел свойство с именем FirstName , ожидаемое входящее значение будет, например, foo.FirstName = John .

Однако, если механизм связывания не видит * foo. ** как часть запроса, он просто ищет * (без префикса foo ). Так что, пока в запросе не было * foo. **, вы можете отправить FirstName = John , и механизм связывания поймет это правильно. Но если в запросе есть * foo. **, значение FirstName = John не будет соответствовать свойству FirstName.

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

...