ASP.NET MVC ViewModel с рекомендациями по выборам - PullRequest
14 голосов
/ 25 октября 2011

Я заметил, что в приложении NerdDinner, если ModelState недопустим для ужина, он просто возвращает представление для модели:

        if (ModelState.IsValid) {
            ...
            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }

        return View(dinner);

Однако в моем приложении модель (модель представления в этой ситуации) содержит несколько списков выбора. На данный момент эти списки не создаются, потому что эта модель представления была просто заполнена при отправке формы. Каков рекомендуемый способ заполнить эти списки выбора перед отправкой их обратно пользователю?

Это то, что я хочу, чтобы мой контроллер делал:

public ActionResult Save(MyModel model)
{
    if (ModelState.IsValid)
    {
        businessClass.Save(model);
        return RedirectToAction("Index", "Home");
    }

    // This won't work because model has uninstantiated SelectLists
    return View("MyView", model);
}

Я не хочу отправлять модель в мою бизнес-логику, если ModelState недействителен, но, похоже, не имеет смысла помещать код заполнения SelectList в мой контроллер. Должен ли я создать публичный метод в моей бизнес-логике исключительно для того, чтобы делать подобные вещи с моими моделями представления?

Ответы [ 5 ]

15 голосов
/ 25 октября 2011

Лично мне нравится быть простым: -

[HttpGet]
public Edit(int id) {
     EditForm form = new EditForm();
     // Populate from the db or whatever...
     PopulateEditPageSelectLists(form);
     return View(form);
}

[HttpPost]
public Edit(EditForm form) {
     if (ModelState.IsValid) {
         // Do stuff and redirect...
     }
     PopulateEditPageSelectLists(form);
     return View(form);
}

public void PopulateEditPageSelectLists(form) {
     // Get lookup data from the db or whatever.
}

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

5 голосов
/ 26 октября 2011

Вы не говорите, сколько многократного использования вы хотели бы.Но лично мне нравятся «чистые» (не вторгающиеся в контроллер) и пригодные для повторного использования, а в MVC это означает - фильтры.

Посмотрите на это:

public class SupplyLanguagesAttribute : System.Web.Mvc.ActionFilterAttribute
{
    public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData["languagesList"] =
            someService.LoadLanguagesAsDictionary();

        base.OnActionExecuting(filterContext);
    }
}

тогда вы просто используетеэто для каждого метода действия, где вам «могут» понадобиться языки:

[SupplyLanguages]
public ActionResult DoSomething()
{
...
}

И затем вы можете использовать данные напрямую для DropDownList из ViewData или даже «обернуть» их (и избежать"Волшебные струны" в представлениях), с возможностью многократного использования DropDown:

public static MvcHtmlString LanguageDropDown(this HtmlHelper html, string name, object selectValue, bool defaultOption = false)
    {
        var languages = html.ViewData["languagesList"] as IDictionary<string,string>;

        if (languages == null || languages.Count() == 0)
            throw new ArgumentNullException("LanguageDropDown cannot operate without list of languages loaded in ViewData. Use SupplyLanguages filter.");

        var list = new SelectList(languages, "Key", "Value", selectValue);

        return SelectExtensions.DropDownList(html, name, list);
    }
1 голос
/ 25 октября 2011

Мои контроллеры заполняют списки выбора в моей модели, если ModelState недопустим.

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

Пример

public ActionResult Save(MyModel model) 
{ 
    if (ModelState.IsValid) 
    { 
        businessClass.Save(model); 
        return RedirectToAction("Index", "Home"); 
    } 
    model.PossibleEmployees 
             = _employeeRepository.All().Select(e => 
                                                new SelectListItem{Text=e.Name, 
                                                                   Value=e.Id});
    return View("MyView", model); 
} 

Обновление

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

0 голосов
/ 08 апреля 2015

Что я делаю, так это то, что у меня есть статическая функция в классе, которая возвращает SelectList. Метод принимает значение Enum, которое определяет, какой SelectList возвращать. В View функции DropDownList или DropDownListFor вызывают эту функцию, чтобы получить SelectList.

Статическая функция выглядит так:

class HelperMethods
{
  enum LookupType {Users, Companies, States};

  public static SelectList CommonSelectList(LookupType type, int? filterValue = null)
    //filterValue can be used if the results need to be filtered in some way
    var db = new WhateverEntities();

    switch (type)
    {  
       case LookupType.Users:
         var list = db.Users.OrderBy(u => u.LastName).ToList()
         return new SelectList(list, "ID", "FullName")
         break;

       case LookupType.Companies
         var list = db.Companies.OrderBy(u => u.Name).ToList()
         return new SelectList(list, "ID", "Name")
         break;

       //and so on...
    }
  }
}

И представление содержит это:

@Html.DropDownListFor(m => m.UserID, HelperMethods.CommonSelectList(LookupType.Users))

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

0 голосов
/ 25 октября 2011

Я использую для заполнения списков, даже если модель недействительна. Еще одно возможное решение - получить действие, возвращающее информацию json, и создать выборку с помощью ajax. Иногда я также прибегал к статическим свойствам / кешированным коллекциям. Я думаю, это всегда зависит от конкретного случая.

PS: Вы можете использовать локальную модель в каждом действии, поэтому я могу оставить инициализацию внутри конструктора модели. (часто я переопределяю базовую модель с помощью [NonAction] утилит).

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

Я добавил некоторый служебный метод в базовом контроллере для создания SelectListItems и тому подобного. Поскольку каждая модель наследуется от базовой, я нашел их почти везде в приложении. Конечно, коллекция заполняется через специальный бизнес-объект.

...