Что такое модель представления?
Назначение модели представления понятно и без объяснений - эта модель специально разработана для использования в представлении. Она предоставляет упрощенный интерфейс над доменной моделью, который сводит принятия решений в представлении к минимуму.
В этом разделе мы разберем работу модели представления на упрощенном примере интернет-магазина. Мы рассмотрим, как модель представления отличается от доменной модели и какие механизмы обеспечивают связь модели представления и самого представления. В конце главы мы изучим модели ввода, которые используются для отправки пользовательских данных от представления к контроллеру.
Пример интернет-магазина
Давайте рассмотрим простой интернет-магазин. Он может содержать такие классы, как Customer
, Order
и Product
(что показано в листинге 5.1), которые соответствуют таблицам в реляционной базе данных и связаны с ними по технологии ORM.
Листинг 5-1: КлассыCustomer
,Order
иProduct
, используемые в интернет-магазине
public class Customer
{
public int Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Active { get; set; }
public ServiceLevel ServiceLevel { get; set; }
public IEnumerable<Order> Orders { get; set; }
}
public enum ServiceLevel
{
Standard,
Premier
}
public class Order
{
public DateTime Date { get; set; }
public IEnumerable<Product> Product { get; set; }
public decimal TotalAmount { get; set; }
}
public class Product
{
public string Name { get; set; }
public decimal Cost { get; set; }
}
Административная часть нашего магазина может содержать страницу сводки по клиентам, которая содержит список всех клиентов и количество их заказов. Пример страницы показан на рисунке 5.1.
Один из способов создать такой интерфейс заключается в использовании доменной модели напрямую. Мы могли бы получить список клиентов из базы данных, передать его в представление, которое на его основе построит таблицу. Когда представление дойдет до последней колонки (Most Recent Order Date), ему нужно будет снова пройтись циклом по коллекции объектов Orders
класса Customer
, чтобы вычислить, какой заказ был самым последним.
Рисунок 5-1: Страница со списком клиентов и заказов

Единственная проблема такого подхода заключается в том, что он сильно усложняет представление. Чтобы сделать представление более регулируемым, нужно сделать его более простым – сложные циклы и расчеты должны выполняться на более высоком уровне. Единственное, что должно выполнять представление - это показывать результаты этих расчетов. Мы создадим модель, которая будет только представлять эту таблицу.
Создание модели представления
Создание модели представления для страницы сводки о клиентах - довольно простая задача. Модели представления, как правило, являются довольно простыми объектами с однообразной структурой, которые обращаются непосредственно к тем данным в БД, которые будут отображаться в пользовательском интерфейсе. В этом случае, наша модель будет просто содержать атрибут для каждого столбца в таблице, как показано в листинге 5.2.
Листинг 5-2: Класс CustomerSummary
public class CustomerSummary
{
public string Name { get; set; }
public string Active { get; set; }
public string ServiceLevel { get; set; }
public string OrderCount { get; set; }
public string MostRecentOrderDate { get; set; }
}
Строки 3-7: Каждый атрибут соответствует столбцу таблицы
Эта модель намеренно проста, она содержит в основном строки (string
). В конце концов, это именно то, что она представляет: текст на странице. Логика отображения данных в этом объекте тоже проста - представление только выводит данные. Модель представления выполняет свою основную задачу - сводит к минимуму принятие решений в представлении.
Модель для всей таблицы имеет тип IEnumerable<CustomerSummary>
. В такой простой модели, как эта, представление только выполняет заданный цикл, записывая данные каждого CustomerSummary
в строки. Но прежде, чем мы сможем отобразить объекты CustomerSummary
, мы должны приписать им необходимые значения и заполнить их данными из нашей доменной модели.
Презентационная модель
В нашем приложении мы будем строить эту модель не один раз. Она может быть или создана путем отправки запроса к базе данных (создание простого отчета), или спроектирована из нашей доменной модели вручную либо автоматически, с помощью такого инструмента отображения, как AutoMapper (который мы рассмотрим в главе 11).
Как правило, создается специальный класс, единственная функция которого – создавать презентационную модель. Лучше строить презентационную модель в коде приложения, чем в представлении, которое должно быть сфокусировано на HTML и стилях. Специальный класс, который создает эту модель, легко программировать, тестировать и поддерживать.
Создавать презентационную модель в контроллере также не рекомендуется. Функция контроллера – вызывать представления и координировать их работу. В листинге 5.3 показан упрощенный пример того, как контроллер может отправить презентационную модель к представлению.
Листинг 5-3: Метод контроллера для презентационной модели
public class CustomerSummaryController : Controller
{
private CustomerSummaries _customerSummaries = new CustomerSummaries();
public ViewResult Index()
{
IEnumerable<CustomerSummary> summaries = _customerSummaries.GetAll();
return View(summaries);
}
}
Строка 9: Отправляет презентационную модель к представлению
В этом примере объект CustomerSummaries
отвечает за создание презентационной модели CustomerSummary
. Он отправляет запрос к доменной модели и отображает результаты в простой форме, подходящей для использования в представлении.
После того, как объекты CustomerSummary
были созданы, контроллер передает их в метод View ()
, который передает представлению (строка 9 листинга). В ASP.NET MVC существует специальный механизм для совместного использования моделей, который мы рассмотрим в следующем пункте.
ViewData.Model
Контроллер и представление совместно используют объект типа ViewDataDictionary
- ViewData
. ViewData
представляет собой стандартный словарь со строковыми индикаторами и значениями объектов, который также имеет атрибут Model
. Когда мы вызываем return View (summaries)
в листинге 5.3, ViewData.Model
автоматически заполняется объектами из нашего списка CustomerSummary
, подготовленными для отображения в представлении. Атрибут Model
является строго типизированным, так что представление точно знает, какой результат от него ожидать. Также разработчики могут воспользоваться преимуществами IDE, такими как IntelliSense и разрешить изменение имен переменных. Большинство из этих внутренних доработок маскируются с помощью движка представлений Razor, который позволяет легко определить тип модели. Тип модели в представлении можно назначить с помощью инструкции @model
:
@model IEnumerable<DisplayModel.Models.CustomerSummary>
Инструкция @model
указывает, что модель представления (свойство ViewData.Model
) принадлежит типу IEnumerable<CustomerSummary>
. Когда мы разработали модель представления, мы можем легко сделать разметку HTML, как показано в листинге 5.4.
Листинг 5-4: Использование модели в представлении
<table>
<tr>
<th>Name</th>
<th>Active?</th>
<th>Service Level</th>
<th>Order Count</th>
<th>Most Recent Order Date</th>
</tr>
@foreach (var summary in Model)
{
<tr>
<td>@summary.Name</td>
<td>@summary.Active</td>
<td>@summary.ServiceLevel</td>
<td>@summary.OrderCount</td>
<td>@summary.MostRecentOrderDate</td>
</tr>
}
</table>
Строка 9: Определяет тип
IEnumerable<CustomerSummary>
Строки 12-16: Работает с моделью
Разметка в листинге 5.4 создает таблицу. Не используя «магические» строковые индикаторы и сложную логику, мы будем работать напрямую с моделью. Разрабатывая модель отдельно и исключительно для представления, можно значительно упростить разработку приложения.
Многие интерфейсы гораздо более сложны, чем одна таблица. Они могут включать несколько таблиц, дополнительные поля с разнообразным контентом: изображения, заголовки, промежуточные итоги, графики, диаграммы и тысячу других вещей, которые усложняют представление. Модель представления создается для обработки их всех. Разработчики могут создавать и поддерживать даже самые сложные интерфейсы, когда модель представления хорошо реализована. Если интерфейс содержит несколько сложных элементов, модель представления может работать как упаковщик, объединяя все модули и значительно упрощая HTML-разметку. Хорошая модель представления не скрывает сложные элементы, но она представляет их как можно более точно и просто и отделяет от других данных на странице.
Еще одной сложной функцией веб-приложения является обработка данных, вводимых пользователем. Мы рассмотрим представление пользовательского ввода в следующей главе.