Pull to refresh

Организуем view models в ASP.NET MVC

Reading time4 min
Views32K


В интернете полно примеров вроде «Делаем вики на ASP.NET MVC за 15 минут». Проблема таких примеров в том, что они используют VideData или ViewBag для передачи данных в View. Для генерации форм используются нетипизированные методы вроде Html.TextBox(). А для получения данных из форм просто параметры к методам контроллера, или хуже того – сами сущности из ORM.

Это может быть прекрасно с точки зрения того, кто в жизни имеет дело только с созданием таких «видео уроков». Но в немного более сложных случаях вы, конечно же, захотите иметь строго типизированные модели, использовать строго типизированные методы вроде Html.TextBoxFor(m=>..), и получать в методе контроллера из формы ровно то, что хотите получить и при этом держать все модели в консистентном виде.

Итак, правила, если вы хотите разрабатывать в MVC:

  • Каждому View свой персональный класс ViewModel.
  • Только View диктует, какие будут свойства у ViewModel. Остальное – проблемы контроллера.
  • ViewModel – это простой DTO, без логики.
  • View использует только те данные, которые приходят из ViewModel, ничего больше. Не трогайте Request.IsAuthenticated в ваших View, для этого есть модель.

Может возникнуть вопрос, а как быть с _Layout (мастер страницами) – ведь это тоже View и они тоже хотят свои модели. Вполне резонно – ведь мы помним, что view диктует модель, всё остальное следует потом. Поэтому создайте в приложении класс SharedLayoutViewModel с нужными вашему _Layout.cshtml свойствами, и наследуйте от него ваши модели для остальных View. Пользуйтесь Result-фильтрами чтобы наполнять эту модель в одном месте для всех методов ваших контроллеров. Или переопределите OnResultExecuting в базовом классе ваших контроллеров, и делайте это там.

Для удобства организуйте ваши модели в директории. Вполне жизнеспособным выглядит вариант такой структуры:


Обрабатываем формы


Попробуем создать контроллер, модель и view для такой формы:


Для этого мы добавим View, в качестве модели которого укажем AccountRegisterViewModel:
@model AccountRegisterViewModel
<h2>
  Register</h2>
@using (Html.BeginForm())
{
  @Html.ValidationSummary()
  <label>
    Name
    @Html.TextBoxFor(m => m.Form.Name)
  </label>
  <label>
    State
    @Html.DropDownListFor(m => m.Form.State, Model.StateSelectList)
  </label>
  <input type="submit" value="Register me" />
}

Добавим в проект в директорию ViewModels/Account класс модели, который включает в себя данные для выпадающего списка State и свойство с самой формой (здесь одна, а в жизни их может быть несколько в одном View):
public class AccountRegisterViewModel : SharedLayoutViewModel<br/>
{<br/>
    public AccountRegisterForm Form { get; set; }<br/>
 <br/>
    public IEnumerable<SelectListItem> StateSelectList { get; set; }<br/>
}

Для нашей формы мы определим отдельный класс. Он будет включать себя нужные атрибуты DataAnnotations для валидации формы, и может быть, даже когда-нибудь реализует IValidatableObject:
public class AccountRegisterForm<br/>
{<br/>
    [Required]<br/>
    public string Name { get; set; }<br/>
 <br/>
    [Required]<br/>
    public string State { get; set; }<br/>
}

Сам контроллер тога будет выглядеть так:
[HttpGet]<br/>
public ActionResult Register()<br/>
{<br/>
    var model = new AccountRegisterViewModel();<br/>
 <br/>
    SetupRegisterViewModel(model);<br/>
    return View(model);<br/>
}<br/>
 <br/>
[HttpPost]<br/>
public ActionResult Register([Bind(Include = "Form")]AccountRegisterViewModel model)<br/>
{<br/>
    if (ModelState.IsValid)<br/>
    {<br/>
        // save model.Form <br/>
 <br/>
        TempData["Message"] = "Thank you for registering";<br/>
        return RedirectToAction("Index");<br/>
    }<br/>
 <br/>
    SetupRegisterViewModel(model);<br/>
    return View(model);<br/>
}<br/>
 <br/>
private void SetupRegisterViewModel(AccountRegisterViewModel model)<br/>
{<br/>
    model.StateSelectList = new SelectList(new[] { string.Empty"NY""VA" });<br/>
}


Вот и всё.

Надеюсь, этот пример станет полезным разработчикам ASP.NET MVC, особенно тем, кто создаёт на нём приложения, а не видео уроки.

P.S. Отмечу, что выбранный пример для этого подхода не показательный. Форма из двух полей без форматирования выбрана только ради экономии времени читателя.
Tags:
Hubs:
+18
Comments60

Articles