MVC — контроллер с несколькими списками выбора

Есть ли хорошие способы сделать мои контроллеры проще, когда у них есть модели, зависящие от большого количества списков выбора? Я стараюсь, чтобы большинство действий моего контроллера были как можно более простыми (надеюсь, не более 10 или около того строк), но на страницах, требующих большого количества раскрывающихся списков, мои действия обычно превышают это:

public class Model
{
    public IEnumerable<SelectListItem> AllLocations { get; set; }
    public IEnumerable<SelectListItem> TopLocations { get; set; }
    public IEnumerable<SelectListItem> AllTemplates { get; set; }
    public IEnumerable<SelectListItem> TopTemplates { get; set; }
    // ...
}

[HttpGet]
public ActionResult Index(int id)
{
    var domain = Repository.Get(id);
    var model = Mapper.Map<Domain, ViewModel>(item);

    // any way to abstract this type of code?
    model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
    model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
    model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
    model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
    // etc. etc.

    return View(model);
}

[HttpPost]
public ActionResult Index(ViewModel model)
{
    // any way to abstract this type of code?
    model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
    model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
    model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
    model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
    // etc. etc.

    return View(model);
}

person Dismissile    schedule 03.02.2012    source источник
comment
В общем, если вы хотите сократить код контроллера, вы можете сократить действие get до одной строки, если вы использовали привязку модели для преобразования идентификатора в объект домена, а затем использовали результат пользовательского действия для сопоставления и обогащения модели. Думаю, многое из этого описано в книге MVC 3 в действии.   -  person GraemeMiller    schedule 05.02.2012


Ответы (3)


Как вы говорите, небольшие действия контроллера — это здорово. Как говорит Джимми Богард посадите свои контроллеры на диету!

Я использую IModelEnricher в сочетании с Automapper. Я возвращаю сущность и т. д., используя определенный ActionResult, который затем автоматически сопоставляет мою сущность с ViewModel и обогащает данными, необходимыми для списков выбора (и любыми дополнительными данными). Этот метод сохраняет ваш код СУХИМ, а контроллеры тонкими, как супермодель :-)! Кроме того, сохранение данных списка выбора как части вашей ViewModel позволяет четко определить обязанности вашего контроллера, модели и представления.

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

В моем случае это выглядит примерно так в контроллере:

public virtual ActionResult Edit(int id)
{
    return AutoMappedEnrichedView<PersonEditModel>(_personRepository.Find(id));
}

[HttpPost]
public virtual ActionResult Edit(PersonEditModel person)
{
     if (ModelState.IsValid){
            //This is simplified (probably don't use Automapper to go VM-->Entity)
            var insertPerson = Mapper.Map<PersonEditModel , Person>(person);
            _personRepository.InsertOrUpdate(insertPerson);
            _requirementRepository.Save();
            return RedirectToAction(Actions.Index());
      }
     return EnrichedView(person);
 }

Этот вид ViewModel:

public class PersonEditModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public int FavouriteTeam { get; set; }
    public IEnumerable<SelectListItem> Teams= new List<SelectListItem>();
}

С этим типом обогащения:

public  class PersonEditModelEnricher :
IModelEnricher<PersonEditModel>
{
    private readonly ISelectListService _selectListService;

    public PersonEditModelEnricher(ISelectListService selectListService)
    {
        _selectListService = selectListService;
    }

    public PersonEditModelEnrich(PersonEditModel model)
    {
        model.Teams = new SelectList(_selectListService.AllTeams(), "Value", "Text")
        return model;
    }
} 

Еще один вариант — украсить ViewModel атрибутами, определяющими расположение данных для заполнения списка выбора. Нравится:

  public class PersonEditModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public int FavouriteTeam { get; set; }
        [LoadSelectListData("Teams")]
        public IEnumerable<SelectListItem> Teams= new List<SelectListItem>();
    }

Теперь вы можете украсить соответствующий метод в выбранной вами службе атрибутом, например:

   [ProvideSelectData("Teams")]
   public IEnumerable Teams()
   {
        return _teamRepository.All.ToSelectList(a => a.Name, a => a.TeamId);
   }

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

См. этот вопрос. Также этот сообщение в блоге и это . Также этот вопрос на форуме Automapper

person GraemeMiller    schedule 04.02.2012

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

eg:

public class SelectHelper
{
     public static List<SelectListItem> AllLocations()
     {
         //TODO repository.GetAllLocations()
     }
     public static List<SelectListItem> TopLocations()
     {
         //TODO repository.GetTopLocations()
     }
     ...
}

код просмотра: @Html.DropDownList("selectname", SelectHelper.AllLocations())

person javan    schedule 04.02.2012
comment
Я бы предпочел, чтобы ViewModel содержал все данные, необходимые для построения представления. Вместо того, чтобы представление отвечало за получение данных. - person GraemeMiller; 04.02.2012
comment
ViewModel — хороший способ. Иногда я часто оставляю ViewModel простым, просто DTO. Далее я сделаю это в ViewModel. Если многие ViewModels имеют этот список выбора, я думаю, что необходимо инкапсулировать метод. - person javan; 04.02.2012
comment
Я просто не думаю, что представление должно получать данные независимо от контроллера. Контроллер должен действовать между представлением и данными. Однако это, очевидно, зависит от размера и сложности приложения. - person GraemeMiller; 04.02.2012

Конечно, просто преобразуйте его в метод, например:

public class Model
{
    public IEnumerable<SelectListItem> AllLocations { get; set; }
    public IEnumerable<SelectListItem> TopLocations { get; set; }
    public IEnumerable<SelectListItem> AllTemplates { get; set; }
    public IEnumerable<SelectListItem> TopTemplates { get; set; }
    // ...
}

[HttpGet]
public ActionResult Index(int id)
{
    var domain = Repository.Get(id);
    var model = Mapper.Map<Domain, ViewModel>(item);
    InitializeSelectLists(model);

    return View(model);
}

[HttpPost]
public ActionResult Index(ViewModel model)
{
    InitializeSelectLists(model);
    View(model);
}


private void InitializeSelectLists(Model model)
{
    model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
    model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
    model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
    model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
    // etc. etc.
}

Или вы даже можете сделать это в конструкторе для вашей модели или службы фасада, если хотите.

person shuniar    schedule 03.02.2012