DropDownListFor и свойства навигации

У меня возникла проблема с использованием @Html.DropDownListFor().

У меня есть модель со свойством навигации:

public class Thing {
  ...
  public virtual Vendor Vendor { get; set; }
}

В контроллере я беру список поставщиков, чтобы бросить его в ViewBag:

public ActionResult Create() {
  ViewBag.Vendors = Vendor.GetVendors(SessionHelper.CurrentUser.Unit_Id);
  return View();
}

HTML-элемент в представлении выглядит так:

@Html.DropDownListFor(model => model.Vendor, new SelectList(ViewBag.Vendors, "Id", "Name"), "---- Select vendor ----")
@Html.ValidationMessageFor(model => model.Vendor)

Выпадающий список отображается, и все выглядит нормально, пока я не отправлю форму. Метод HttpPost Create возвращает значение false для ModelState.IsValid и выдает ошибку модели: Преобразование параметра из типа «System.String» в тип «...Models.Vendor» не удалось, так как ни один преобразователь типов не может преобразовать эти типы.

Если я пропущу публикацию страницы, я получу ошибку сервера: Сведения об исключении: System.ArgumentNullException: значение не может быть нулевым. Имя параметра: элементы

После поиска вверху и внизу я не смог найти причину, по которой @Html.DropDownListFor() неправильно автоматически привязывает объект Vendor к свойству навигации.

Любая помощь будет принята с благодарностью.

РЕДАКТИРОВАТЬ: мне пришлось явно установить атрибуты ForeignKey, чтобы я мог напрямую получить доступ к «Vendor_Id», затем я изменил DropDownListFor, чтобы указать на «Vendor_Id» вместо свойства навигации. Кажется, это работает.


person Kizmar    schedule 09.08.2011    source источник


Ответы (2)


Я обнаружил, что лучший способ сделать это заключается в следующем. Измените контроллер, чтобы создать SelectListItems.

   public ActionResult Create() {
       ViewBag.Vendors = Vendor.GetVendors(SessionHelper.CurrentUser.Unit_Id)
                               .Select(option => new SelectListItem
                               {
                                  Text = (option == null ? "None" : option.Name),
                                  Value = option.Id.ToString()
                               });
       return View();
    }

Затем измените представление следующим образом:

@Html.DropDownListFor(model => model.Vendor, (IEnumerable<SelectListItem>)ViewBag.Vendors,    "---- Select vendor ----")
@Html.ValidationMessageFor(model => model.Vendor)

Вы должны указать ViewBag.Vendors как (IEnumerable).

Это сохраняет вид красивым и аккуратным. Вы также можете переместить код, который получает SelectListItems, в свой репозиторий и поместить его в метод, называемый чем-то вроде GetVendorsList().

public IEnumerable<SelectListItem> GetVendorsList(int unitId){
    return Vendor.GetVendors(unitId)
                               .Select(option => new SelectListItem
                               {
                                  Text = (option == null ? "None" : option.Name),
                                  Value = option.Id.ToString()
                               });
}

Это хорошо отделит проблемы и сохранит ваш контроллер в чистоте.

Удачи

person TheGwa    schedule 06.10.2011
comment
Вы не должны использовать ViewBag для передачи данных представления. Вы должны использовать модель представления. Я не могу разместить здесь код, он не читается. Пишите мне в личку и я дам вам знать ;) - person Ninglin; 14.07.2015
comment
Согласованный. Это было давно. Я думаю о MVC 3. Модели просмотра, безусловно, путь. - person TheGwa; 15.07.2015

Я ответил на аналогичный вопрос в следующем вопросе stackoverflow. Ответ хорош и для этого вопроса.

Проверка свойств навигации в MVC (4 ) и КВ (4)

Этот подход не публикует SelectList в контроллере. Я не думаю, что публикация SelectList в контроллере — хорошая идея, потому что это означает, что мы заботимся о части представления в контроллере, что явно не является разделением задач.

person Bikash Bishwokarma    schedule 27.05.2016