Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

@Html.ValidationSummary()

<p>Your name: @Html.TextBoxFor(x => x.Name) </p> <p>Your email: @Html.TextBoxFor(x => x.Email)</p> <p>Your phone: @Html.TextBoxFor(x => x.Phone)</p> <p>

Will you attend?

@Html.DropDownListFor(x => x.WillAttend, new[] {

new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString},

new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option")

</p>

<input type="submit" value="Submit RSVP" />

}

</body>

</html>

Если ошибок нет, метод Html.ValidationSummary создает скрытый элемент списка в качестве заполнителя в форме. MVC делает метку-заполнитель видимой и добавляет сообщения об ошибке, определяемые атрибутами валидации. Вы можете увидеть, как это выглядит, на рисунке 2-19.

Рисунок 2-19: Сводка результатов валидации

Пользователю не будет показано представление Thanks, пока не будут удовлетворены все правила валидации, которые мы применили к классу GuestResponse. Обратите внимание, что данные, введенные в форму, были сохранены и отображаются снова, когда представление показано со сводкой валидации. Это еще одно преимущество связывания данных.

Примечание

41

Если вы работали с ASP.NET Web Forms, вы знаете, что в Web Forms существует понятие «серверные элементы управления», которые сохраняют состояние, сериализуя значения в скрытом поле формы __VIEWSTATE. Связывание данных

ASP.NET MVC не привязано к Web Forms концепции серверных элементов управления, обратной передачи данных или View State. ASP.NET MVC не вводит скрытое поле __VIEWSTATE в обрабатываемые HTML страницы.

Выделение невалидных полей

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

Если свойство класса модели не прошло валидацию, вспомогательные методы HTML будут генерировать немного другой HTML. В качестве примера, вот HTML, который генерирует вызов

Html.TextBoxFor (х => x.Name), когда нет ошибки валидации:

<input data-val="true" data-val-required="Please enter your name" id="Name" name="Name"

type="text" value="" />

А вот HTML, который генерирует тот же вызов, когда пользователь не предоставил значение (что является ошибкой валидации, потому что мы применили атрибут Required свойства Name в классе модели GuestResponse):

<input class="input-validation-error" data-val="true" data-val-required="Please enter your

name" id="Name" name="Name" type="text" value="" />

Мы выделили различия. Это вспомогательный метод добавил класс с именем input-validation- error. Мы можем воспользоваться этой функцией, создав таблицу стилей CSS, которая содержит стили для этого класса и других, что применяют различные вспомогательные методы HTML.

Соглашение в MVC проектах заключается в том, что статический контент, такой как таблицы стилей CSS, помещается в папку под названием Content. Мы создали папку Content, нажав правой кнопкой мыши по проекту PartyInvites в Solution Explorer и выбрав из всплывающего меню Add New Folder. Мы создали таблицу стилей, щелкнув правой кнопкой мыши по папке

Content, выбрав Add New Item и затем выбрав Style Sheet в диалоговом окне Add New Item. Мы назвали нашу таблицу стилей Site.css, и это имя, которое Visual Studio использует при создании проекта с использованием иного шаблона MVC, а не Empty. Вы можете посмотреть содержимое файла Content/Site.css в листинге 2-19.

Листинг 2-19: Содержимое файла Content/Site.css

.field-validation-error {color: #f00;}

.field-validation-valid { display: none;}

.input-validation-error { border: 1px solid #f00; background-color: #fee; }

.validation-summary-errors { font-weight: bold; color: #f00;}

.validation-summary-valid { display: none;}

42

Чтобы использовать эту таблицу стилей, мы добавили новую ссылку в раздел head представления RsvpForm, как показано в листинге 2-20. Вы добавляете представлениям элементы link так же, как в обычном статическом HTML файле.

Листинг 2-20: Добавление элемента link в представление RsvpForm

@model PartyInvites.Models.GuestResponse

@{

Layout = null;

}

<!DOCTYPE html> <html>

<head>

<meta name="viewport" content="width=device-width" />

<link rel="stylesheet" type="text/css" href="~/Content/Site.css" />

<title>RsvpForm</title>

</head>

<body>

@using (Html.BeginForm())

{

@Html.ValidationSummary()

<p>Your name: @Html.TextBoxFor(x => x.Name) </p> <p>Your email: @Html.TextBoxFor(x => x.Email)</p> <p>Your phone: @Html.TextBoxFor(x => x.Phone)</p> <p>

Will you attend?

@Html.DropDownListFor(x => x.WillAttend, new[] {

new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString},

new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option")

</p>

<input type="submit" value="Submit RSVP" />

}

</body>

</html>

Совет

Если вы использовали MVC 3, то могли ожидать, чтобы мы добавим CSS файл к представлению, указав атрибут href как @Href("~/Content/Site.css") или

@Url.Content("~/Content/Site.css"). С MVC 4 Razor автоматически обнаруживает атрибуты, которые начинаются с ~/, и автоматически вставляет для вас @Href или @Url.

Теперь будет отображаться визуально более очевидная ошибка валидации, если были представлены данные, которые вызвали эту ошибку, как показано на рисунке 2-20.

Рисунок 2-20: Автоматически выделенные ошибки валидации

43

Завершаем пример

Последнее требование к нашему примеру приложения заключается в том, чтобы отправить имейл с завершенными RSVP нашему другу, организатору вечеринки. Мы могли бы сделать это, добавив метод действия, чтобы создать и отправить по электронной почте сообщение, используя e-mail классы .NET Framework. Вместо этого мы собираемся использовать вспомогательный метод WebMail. Это не входит в рамки MVC, но это позволит нам завершить данный пример, не увязнув в деталях создания других средств отправки электронной почты.

Примечание

Мы использовали вспомогательный метод WebMail, потому что он позволяет

нам с минимальными усилиями продемонстрировать отправку сообщений электронной почты. Однако, в обычной ситуации мы предпочли бы переложить эту функцию на метод действия. Мы объясним, почему, когда будем описывать архитектурный MVC паттерн в главе 3.

Мы хотим, чтобы имейл сообщение было отправлено, когда мы обрабатываем представление Thanks. В листинге 2-21 показаны изменения, которые мы должны сделать.

Листинг 2-21: Использование вспомогательного метода WebMail

@model PartyInvites.Models.GuestResponse

@{

Layout = null;

}

<!DOCTYPE html> <html>

44

<head>

<meta name="viewport" content="width=device-width" /> <title>Thanks</title>

</head>

<body>

@{ try

{

WebMail.SmtpServer = "smtp.example.com"; WebMail.SmtpPort = 587; WebMail.EnableSsl = true; WebMail.UserName = "mySmtpUsername"; WebMail.Password = "mySmtpPassword"; WebMail.From = "rsvps@example.com";

WebMail.Send("party-host@example.com", "RSVP Notification", Model.Name + " is " + ((Model.WillAttend ?? false) ? "" : "not")

+ "attending");

}

catch (Exception)

{

@:<b>Sorry - we couldn't send the email to confirm your RSVP.</b>

}

}

<div>

<h1>Thank you, @Model.Name!</h1> @if (Model.WillAttend == true)

{

@:It's great that you're coming. The drinks are already in the fridge!

}

else

{

@:Sorry to hear that you can't make it, but thanks for letting us know.

}

</div>

</body>

</html>

Мы добавили Razor выражение, которое использует вспомогательный метод WebMail для настройки информации о нашем сервере электронной почты, включая имя сервера, требует ли сервер SSL соединения и данных учетной записи. После того как мы все настроили, мы используем метод WebMail.Send для отправки электронной почты.

Мы включили весь имейл код в блок try...catch, чтобы мы могли предупредить пользователя, если электронная почта не отправилась. Мы делаем это путем добавления текстового блока к выходным данным представления Thanks. Лучше было бы отобразить отдельное представление ошибки, если сообщение электронной почты не может быть отправлено, но мы хотели не усложнять наше первое MVC приложение.

Резюме

Вэтой главе мы создали новый MVC проект и использовали его, чтобы построить простое MVC приложение по вводу данных, давая вам первое представление об архитектуре MVC Framework и его подходах. Мы пропустили некоторые ключевые особенности (в том числе синтаксис Razor, маршрутизацию (роутинг) и автоматизированное тестирование), но мы вернемся к этим темам в следующих главах.

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

45

MVC паттерн

В главе 7 мы собираемся начать строить более сложный ASP.NET MVC пример. Прежде чем мы начнем копаться в деталях ASP.NET MVC Framework, мы хотим убедиться, что вы знакомы с MVC паттерном и имеете соответствующую философию программирования. В этой главе мы рассмотрим следующее:

Архитектурный MVC паттерн

Доменные модели и хранилища

Создание слабо связанных систем с помощью внедрения зависимости (DI, Dependency injection)

Основы автоматизированного тестирования

Возможно, вы уже знакомы с некоторыми из идей и соглашений, которые мы обсудим в этой главе, особенно, если вы разрабатывали на ASP.NET и C#. Если же нет, то мы советуем вам внимательно прочитать эту главу: хорошее понимание того, что лежит в основе MVC, может помочь вам рассмотреть все возможности фреймворка, которые мы покажем в данной книге.

История MVC

Термин model-view-controller использовался с конца 1970-х и возник из проекта Smalltalk в Xerox PARC, где он был задуман как способ организации некоторых ранних GUI приложений. Некоторые мелкие функции исходного MVC паттерна были привязаны к конкретным понятиям Smalltalk, таким как экраны и инструменты (screens и tools), но более широкие понятия попрежнему применимы к приложениям, и они особенно хорошо подходят для веб приложений.

Взаимодействие с MVC приложением следует естественному циклу действий пользователя и обновлений представлений, где представление считается не сохраняющим состояние (stateless). Это хорошо согласуется с HTTP запросами и ответами, лежащими в основе веб приложений.

Кроме того, MVC заставляет разделять понятия: доменная модель и логика контроллера отделены от пользовательского интерфейса. В веб приложениях это обозначает, что HTML хранится отдельно от остальной части приложения, что делает техническую поддержку и тестирование проще и легче. Ruby on Rails привел к возобновлению интереса к MVC, и он остается образцовым «ребенком» MVC. С тех пор появилось много других MVC фреймворков, которые продемонстрировали преимущества MVC, в том числе, конечно, ASP.NET MVC.

Понимание MVC паттерна

По большому счету MVC паттерн обозначает, что MVC приложение будет разделено как минимум на три части:

Модели, которые содержат или представляют данные, с которыми работают пользователи. Это могут быть простые модели представления, которые только представляют данные, передаваемые от контроллера представлению, или они могут быть доменными моделями, которые содержат данные домена, а также операции, преобразования и правила работы с этими данными.

Представления, которые используются для того, чтобы обработать некоторые части модели в качестве пользовательского интерфейса.

46

Контроллеры, которые обрабатывает входящие запросы, выполняют операции для модели и выбирают представления для показа пользователю.

Модели являются определением вселенной, в которой работает ваше приложение. В банковском приложении, например, модель представляет собой все в банке, что поддерживает приложение: то есть счета, главную книгу и кредитные лимиты для клиентов, а также операции, которые могут быть использованы для манипулирования данными в модели, такие как депонирование средств и осуществление изъятия со счетов. Модель также несет ответственность за сохранение общего состояния и согласованности данных, например, она гарантирует, что все сделки будут добавлены в книгу, и что клиент не сможет снять больше денег, чем он может, или больше денег, чем имеет банк.

Модели также определяются тем, за что они не несут ответственность: модели не занимаются UI и обработкой запросов – это обязанности представлений и контроллеров. Представления содержат логику, необходимую для отображения элементов модели пользователю, и больше ничего. Они не имеют прямого понимания модели и никоим образом напрямую не сообщаются с моделью.

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

Каждый элемент MVC архитектуры является четко определенным и автономным – это называется разделением понятий. Логика, которая манипулирует данными в модели, содержится только в модели; логика, которая отображает данные, находится только в представлении, а код, который обрабатывает запросы пользователей и вводные данные, содержится только в контроллере. С четким разделением обязанностей между каждой из частей ваше приложение будет легче поддерживать и расширять, независимо от того, насколько большим оно будет становиться.

Понимание доменной модели

Наиболее важной частью MVC приложения является доменная модель. Мы создаем модели путем выявления реальных лиц, операций и правил, которые существуют в отрасли или деятельности. Они должны поддерживаться нашим приложением, и они известны как домен.

Затем мы создаем программное представление домена – доменную модель. Для наших целей доменная модель представляет собой набор типов C# (классы, структуры и т.д.), известных под общим названием доменные типы. Операции из домена представлены методами, определенными в доменных типах, а доменные правила выражаются в логике внутри этих методов, или, как мы видели в предыдущей главе, в применении C# атрибутов к методам. Когда мы создаем экземпляр доменного типа, чтобы представить определенный фрагмент данных, мы создаем доменный объект. Доменные модели, как правило, постоянны – есть много разных способов достижения этого, но реляционные базы данных остаются наиболее распространенным выбором.

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

Совет

Распространенный способ обеспечения разделения доменной модели и остальной части ASP.NET MVC приложения заключается в том, чтобы поместить модель в отдельную сборку C#. Таким образом, вы можете создать ссылки на доменную

47

модели из других частей приложения, но нужно убедиться, что в обратном направлении ссылок нет. Это особенно полезно в крупномасштабных проектах. Мы будем использовать такой подход в примере, который мы начнем строить в главе 7.

ASP.NET реализация MVC

В MVC контроллеры являются C# классами, как правило, производными от класса System.Web.Mvc.Controller. Каждый открытый (public) метод в классе, производный от Controller, называется методом действия, который связан с настраиваемым URL через систему маршрутизации (роутингом) ASP.NET. При отправке запроса на URL, связанный с методом действия, исполняются выражения в классе контроллера для того, чтобы выполнить некоторые операции над доменной моделью, а затем выбрать представление для отображения клиенту. На рисунке 3-1 показано взаимодействие между контроллером, моделью и представлением.

Рисунок 3-1: Взаимодействие основных компонентов в MVC приложении

ASP.NET MVC Fr

amework обеспечивает поддержку выбора движков представления. Более ранние версии MVC использовали стандартный ASP.NET движок представления, который обрабатывал ASPX страницы с помощью модернизированной версии синтаксиса разметки Web Forms. MVC 3 ввел движок представления Razor, который был усовершенствован в MVC 4 и который использует полностью другой синтаксис (описанный в главе 5). Visual Studio обеспечивает поддержку IntelliSense для обоих движков представления, что сильно упрощает работу с данными представления, отправленными контроллером.

ASP.NET MVC не применяет никаких ограничений на реализацию вашей доменной модели. Вы можете создать модель с помощью обычных C# объектов и осуществлять хранение при помощи любой из баз данных, объектно-реляционных фреймворков или других инструментов хранения данных, поддерживаемых .NET. Visual Studio создает папку /Models в рамках шаблона MVC проекта. Это подходит для простых проектов, но в более сложных приложениях, как правило, доменные модели определяются в отдельный проект Visual Studio. Мы обсудим реализацию доменной модели далее в этой главе.

Сравнение MVC с другими паттернами

MVC – это не единственный архитектурный паттерн программного обеспечения, конечно, есть много других, и некоторые из них (или по крайней мере были) чрезвычайно популярны. Мы можем многое узнать об MVC, рассмотрев другие паттерны. В следующих разделах мы кратко опишем различные подходы к структурированию приложения и сопоставим их с MVC. Некоторые из паттернов являются близкими вариациями на тему MVC, в то время как другие совершенно отличаются.

48

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

Паттерн Smart UI

Один из наиболее распространенных паттернов известен как smart UI. Большинство программистов в какой-то момент своей карьеры создавали smart UI приложения, мы, конечно же, тоже. Если вы использовали Windows Forms и ASP.NET Web Forms, то и вы тоже.

Чтобы создать smart UI приложение, разработчики выстраивают пользовательский интерфейс, обычно путем перетаскивания набора компонентов или элементов управления на дизайнерскую поверхность. Элементы управления сообщают о взаимодействии с пользователем, представляя события для нажатия кнопок, нажатия клавиш, движения мыши и так далее. Разработчик добавляет код в ответ на эти события в серии обработчиков событий – небольших блоков кода, которые вызываются, когда происходит определенное событие для определенного компонента. И тут мы заканчиваем с монолитным приложением, как показано на рисунке 3-2 – код, который обрабатывает пользовательский интерфейс и логика смешиваются вместе, понятия вообще не разделены. Код, который определяет допустимые значения для вводимых данных, запросов данных или изменяет учетную запись пользователя, делится на маленькие кусочки, соединенные вместе по порядку, в котором ожидаются события.

Рисунок 3-2: Smart UI паттерн

Самым большим недостатком этой конструкции является то, что ее трудно поддерживать и расширять: смешивание доменной модели и кода бизнес логики с кодом пользовательского интерфейса приводит к дублированию, где тот же фрагмент бизнес логики копируется и вставляется для поддержки вновь добавленного компонента. Поиск всех дублирующих частей и внесение правок может быть трудным, а в сложном smart UI приложении иногда почти невозможно добавить новую функцию, не ломая существующую. Тестирование smart UI приложений также может быть трудным, единственный способ – это имитации взаимодействий с пользователем, которая далека от идеала и не обеспечивает основу для полного тестирования.

В мире MVC Smart UI часто называют анти-паттерном, которого следует избегать любой ценой. Эта антипатия возникла, по крайней мере, частично потому, что люди пришли к MVC в поисках альтернативы, проведя часть своей карьеры, пытаясь развивать и поддерживать Smart UI приложения. Это, безусловно, относится и к нам, и мы оба несем в себе следы этих долгих лет, но мы не отвергаем smart UI паттерн полностью. Не все гладко в smart UI паттерне, но есть и положительные аспекты в таком подходе. Smart UI приложения быстро и легко разрабатывать: создатели инструментария приложили много усилий для того, чтобы разработка была приятной, и

49

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

Самая большая слабая сторона Smart UI приложений – сопровождение и поддержка – не видна при разработке небольших приложений. Если вы создаете простой инструмент для небольшой аудитории, то Smart UI приложение может быть идеальным решением. Дополнительная сложность MVC приложений просто не оправдана.

Наконец, Smart UI идеально подходит для создания прототипов пользовательского интерфейса, эти инструменты конструирования действительно хороши. Если вы сидите с клиентом и хотите понять требования к интерфейсу, инструменты Smart UI могут быть быстрым и отзывчивым способом создания и тестирования различных идей.

Архитектура Model-View

Область, в которой, как правило, возникают проблемы с поддержкой в Smart UI приложениях – это бизнес-логика, и внесение изменений может стать очень неприятным процессом. Улучшения в этой области предлагает архитектура model-view (модель-представление), которая вытаскивает бизнес-логику в отдельную доменную модель. При этом данные, процессы и правила все сосредоточены в одной части приложения, как показано на рисунке 3-3.

Рисунок 3-3: model-view паттерн

Архитектура model-view является значительным улучшением по сравнению со Smart UI – например, ее гораздо легче поддерживать. Тем не менее, возникают две проблемы. Первое, поскольку пользовательский интерфейс и доменная модель настолько тесно интегрированы, затрудняется выполнение юнит тестирования. Вторая проблема вытекает из практики, а не из определения паттерна. Эта модель обычно содержит массу кода доступа к данным, а это значит, что модель данных содержит не только бизнес данные, операции и правила.

Классическая трехуровневая (three-tier) архитектура

Для решения проблемы с архитектурой model-view трехуровневая или трехслойная (three-tier или three-layer) модель отделяет код от доменной модели и помещает его в новый компонент под названием DAL. Это показано на рисунке 3-4.

Рисунок 3-4: Трехуровневый паттерн

50

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]