ASP.NET MVC 4

Джесс Ч., Тодд С., Хришикеш П."ASP.NET MVC 4: разработка реальных веб-приложений с помощью ASP.NET MVC", Вильямс, 2013 год, 424 стр.

издательство Вильямс http://www.williamspublishing.com/

 

Книга рассказывает о построении современных серверных приложениях на базе ASP.NET MVC 4 (фреймворк для создания веб-приложений). Хотите иметь современную версию мобильного сайта-тогда эта книга для вас. Она поможет вам разобраться с инфраструктурой и использованием различных сценариев разработки в данном фреймворке. Кроме того ещё вы узнаете о других веб-технологиях:HTML, JavaScript, Entity Framework.

ISBN: ISBN 978-5-8459-1841-3

Содержание

Об авторах 15
Предисловие 16
Предполагаемая читательская аудитория 16
Что требуется для работы с этой книгой 16
Соглашения, используемые в этой книге 17
Использование примеров кода 17
От издательства 17

Часть I. Начало работы 19

Глава 1. Основы ASP.NET MVC 20
Платформа веб-разработки от Microsoft 20
Active Server Pages (ASP) 20
ASP.NET Web Forms 21
ASP.NET MVC 21
Архитектура "модель-представление-контроллер” 21
Модель 22
Представление 22
Контроллер 23
Нововведения, появившиеся в версии ASP.NET MVC 4 23
Введение в EBuy 24
Установка ASP.NET MVC 25
Создание приложения ASP.NET MVC 26
Шаблоны проекта 26
Соглашение по конфигурации 29
Запуск приложения 30
Маршрутизация 30
Конфигурирование маршрутов 31
Контроллеры 33
Действия контроллеров 33
Результаты действий 34
Параметры действий 35
Фильтры действий 37
Представления 38
Определение местоположения представлений 38
Механизм Razor 39
Различение кода и разметки 40
Компоновки 41
Частичные представления 42
Отображение данных 44
Вспомогательные методы HTML и URL 46
Модели 46
Собираем все вместе 47
Маршрут 47
Контроллер 47
Представление 50
Аутентификация 53
Класс AccountController 54
Резюме 55

Глава 2. ASP.NET MVC для разработчиков Web Forms 56
Все это просто ASP.NET 56
Инструменты, языки и API-интерфейсы 56
Обработчики и модули HTTP 57
Управление состоянием 57
Развертывание и исполняющая среда 58
Больше различий, чем сходства 58
Разделение логики приложения и логики представления 59
URL и маршрутизация 59
Управление состоянием 60
Визуализация HTML-разметки 61
Реализация представлений ASP.NET MVC с использованием синтаксиса Web Forms 64
Несколько предостережений 65
Резюме 66

Глава 3. Работа с данными 67
Построение формы 67
Обработка отправок формы 69
Сохранение данных в базе 69
Entity Framework Code First: соглашение по конфигурации 70
Создание уровня доступа к данным с помощью Entity Framework Code First 70
Проверка достоверности данных 71
Указание бизнес-правил с помощью аннотаций данных 72
Отображение сообщений об ошибках проверки достоверности 75
Резюме 77

Глава 4. Разработка на стороне клиента 78
Работа с JavaScript 78
Селекторы 80
Реагирование на события 83
Манипулирование DOM 85
AJAX 86
Проверка достоверности на стороне клиента 88
Резюме 91

Часть II. Переход на следующий уровень 93

Глава 5. Архитектура веб-приложений 94
Шаблон "модель-представление-контроллер” 94
Разделение ответственности 94
MVC и веб-платформы 958 Содержание
Разработка архитектуры веб-приложения 97
Логическое проектирование 97
Логическое проектирование веб-приложения ASP.NET MVC 97
Полезные советы по логическому проектированию 99
Физическое проектирование 100
Пространства имен проекта и имена сборок 100
Варианты развертывания 101
Полезные советы по физическому проектированию 101
Принципы проектирования 103
SOLID 103
Инверсия управления 108
Принцип Don’t Repeat Yourself 115
Резюме 116

Глава 6. Улучшение сайта с помощью AJAX 117
Частичная визуализация 117
Визуализация частичных представлений 118
Визуализация с помощью JavaScript 123
Визуализация данных JSON 123
Запрашивание данных JSON 125
Шаблоны клиентской стороны 125
Повторное использование логики в запросах AJAX и не AJAX 128
Реагирование на запросы AJAX 129
Реагирование на запросы JSON 130
Применение одной и той же логики во множестве действий контроллера 131
Отправка данных на сервер 132
Отправка сложных объектов JSON 133
Выбор связывателя модели 135
Эффективная отправка и получение данных JSON 137
Междоменный AJAX 137
JSONP 137
Включение разделения ресурсов между источниками 141
Резюме 142

Глава 7. ASP.NET Web API 143
Построение службы данных 143
Регистрация маршрутов Web API 145
Соблюдение соглашения по конфигурации 145
Переопределение соглашений 146
Подключение Web API 147
Разбиение на страницы и запрашивание данных 149
Обработка исключений 150
Форматеры носителей 152
Резюме 155

Глава 8. Расширенная работа с данными 156
Шаблоны доступа к данным 156
Традиционные объекты CLR 156
Использование шаблона Repository 157Содержание 9
Объектно-реляционные отображатели 159
Обзор Entity Framework 160
Выбор подхода доступа к данным 161
Параллелизм базы данных 162
Построение уровня доступа к данным 164
Использование подхода Entity Framework Code First 164
Бизнес-модель предметной области EBuy 166
Работа с контекстом данных 169
Сортировка, фильтрация и разбиение данных на страницы 170
Резюме 176

Глава 9. Безопасность 177
Построение защищенных веб-приложений 177
Обеспечьте защиту в глубину 177
Никогда не доверяйте введенным данным 178
Соблюдайте принцип наименьшего уровня привилегий 178
Предполагайте, что внешние системы являются незащищенными 178
Сокращайте поверхность атаки 178
Отключайте ненужные средства 179
Защита приложения 179
Защита интранет-приложения 180
Аутентификация с помощью форм 184
Предохранение против атак 191
Внедрение SQL-кода 192
Межсайтовые сценарии 196
Подделка межсайтовых запросов 197
Резюме 199

Глава 10. Разработка веб-приложений для мобильных устройств 200
Мобильные возможности ASP.NET MVC 4 200
Добавление мобильных возможностей в приложение 202
Создание мобильного представления для аукционных товаров 202
Начало работы с jQuery Mobile 203
Улучшение представления с помощью jQuery Mobile 205
Устранение настольных представлений на мобильном сайте 210
Совершенствование мобильного интерфейса 210
Адаптивная визуализация 210
Дескриптор окна просмотра 211
Определение мобильных функциональных возможностей 212
Медиа-запросы CSS 213
Представления, специфичные для браузера 214
Создание нового мобильного приложения с нуля 216
Сдвиг парадигмы в jQuery Mobile 216
Шаблон Mobile Application в ASP.NET MVC 4 216
Использование шаблона Mobile Application инфраструктуры ASP.NET MVC 4 218
Резюме 22010 Содержание

Часть III. Выход за стандартные рамки 221

Глава 11. Параллельные, асинхронные и операции над данными в реальном времени 222
Асинхронные контроллеры 222
Создание асинхронного контроллера 223
Обстоятельства, при которых используются асинхронные контроллеры 225
Асинхронные коммуникации реального времени 225
Сравнение моделей приложений 225
Опрос HTTP 226
Длительный опрос HTTP 227
События, отправляемые сервером 227
Веб-сокеты 228
Расширение возможностей коммуникаций реального времени 229
Конфигурирование и настройка 233
Резюме 234

Глава 12. Кеширование 235
Типы кеширования 235
Кеширование серверной стороны 235
Кеширование клиентской стороны 236
Технологии кеширования серверной стороны 236
Кеширование на уровне запросов 236
Кеширование на уровне пользователей 237
Кеширование на уровне приложений 238
Кеш ASP.NET 238
Кеш вывода 240
"Кеширование бублика” 243
"Кеширование дырки от бублика” 245
Распределенное кеширование 246
Технологии кеширования клиентской стороны 251
Кеш браузера 251
API-интерфейс ApplicationCache 252
Локальное хранилище 254
Резюме 256

Глава 13. Технологии оптимизации клиентской стороны 257
Структура страницы 257
Структура HTTP-запроса 258
Рекомендуемые приемы 259
Делайте меньше HTTP-запросов 260
Используйте сеть доставки контента 260
Добавляйте заголовок Expires или Cache-Control 260
Компоненты, сжатые с помощью GZip 263
Размещайте таблицы стилей в верхней части документа 265
Размещайте сценарии в нижней части документа 265
Делайте сценарии и стили внешними 266
Сокращайте поиск в DNS 267
Минимизируйте сценарии и стили 268Содержание 11
Избегайте перенаправлений 269
Удаляйте дублированные сценарии 270
Конфигурируйте теги ETag 271
Измерение производительности клиентской стороны 272
Ввод в работу ASP.NET MVC 274
Пакетирование и минимизация 275
Резюме 278

Глава 14. Расширенная маршрутизация 279
Нахождение пути 279
URL и поисковая оптимизация 281
Построение маршрутов 282
Стандартные и необязательные параметры маршрута 283
Порядок и приоритет маршрутизации 284
Маршрутизация на существующие файлы 285
Игнорирование маршрутов 285
Универсальные маршруты 286
Ограничения маршрутов 287
Исследование маршрутов с использованием Glimpse 289
Маршрутизация на основе атрибутов 289
Расширение маршрутизации 293
Конвейер маршрутизации 293
Резюме 297
Глава 15. Многократно используемые компоненты пользовательского интерфейса 298
Что ASP.NET MVC предлагает в готовом виде 298
Частичные представления 298
Расширения класса HtmlHelper или специальные вспомогательные
методы HTML 299
Шаблоны отображения и редактирования 299
Html.RenderAction() 299
Продвижение на шаг вперед 300
Генератор одиночных файлов Razor 300
Создание многократно используемых представлений ASP.NET MVC 302
Создание многократно используемых вспомогательных методов ASP.NET MVC 305
Модульное тестирование представлений Razor 307
Резюме 309

Часть IV. Контроль качества 311

Глава 16. Регистрация в журнале 312
Обработка ошибок в ASP.NET MVC 312
Включение средства специальных ошибок 313
Обработка ошибок в действиях контроллеров 313
Определение глобальных обработчиков ошибок 314
Регистрация в журнале и трассировка 316
Регистрация ошибок в журнале 316
Мониторинг работоспособности ASP.NET 318
Резюме 32112 Содержание

Глава 17. Автоматизированное тестирование 322
Семантика тестирования 322
Ручное тестирование 323
Автоматизированное тестирование 324
Уровни автоматизированного тестирования 324
Модульные тесты 324
Интеграционные тесты 327
Приемочные тесты 328
Что собой представляет проект автоматизированных тестов? 329
Создание тестового проекта в Visual Studio 329
Создание и выполнение модульного теста 330
Тестирование приложения ASP.NET MVC 333
Тестирование модели 333
Разработка через тестирование 336
Написание чистых автоматизированных тестов 337
Тестирование контроллеров 339
Рефакторинг модульных тестов 342
Имитация зависимостей 342
Тестирование представлений 347
Покрытие кода 349
Миф о стопроцентном покрытии кода 351
Разработка поддающегося тестированию кода 351
Резюме 353

Глава 18. Автоматизация построения 354
Создание сценариев построения 355
Проекты Visual Studio являются сценариями построения 355
Добавление простой задачи построения 355
Выполнение построения 356
Возможности безграничны! 357
Автоматизация процесса построения 357
Типы автоматизированных построений 358
Создание автоматизированного построения 359
Непрерывная интеграция 362
Обнаружение проблем 362
Принципы непрерывной интеграции 363
Резюме 366

Часть V. Выход в свет 367

Глава 19. Развертывание 368
Что необходимо развертывать 368
Основные файлы веб-сайта 368
Статический контент 370
Что не должно развертываться 370
Базы данных и другие внешние зависимости 371
Что требуется для приложения EBuy 372
Развертывание на сервере IIS 372
Предварительные условия 373Содержание 13
Создание и конфигурирование веб-сайта IIS 373
Публикация из Visual Studio 375
Развертывание в Windows Azure 378
Создание учетной записи Windows Azure 378
Создание нового веб-сайта Windows Azure 379
Публикация веб-сайта Windows Azure через систему управления
исходным кодом 380
Резюме 381

Часть VI. Приложения 383

Приложение А. Интеграция ASP.NET MVC и Web Forms 384
Выбор между ASP.NET MVC и ASP.NET Web Forms 384
Перевод сайта Web Forms на ASP.NET MVC 385
Добавление ASP.NET MVC к существующему приложению Web Forms 386
Копирование функциональности Web Forms в приложение ASP.NET MVC 388
Интеграция функциональности Web Forms и ASP.NET MVC 389
Управление пользователями 389
Управление кешем 389
Многое, многое другое! 389
Резюме 390

Приложение Б. Использование NuGet в качестве платформы 391
Установка инструмента командной строки NuGet 391
Создание пакетов NuGet 392
Файлы NuSpec 392
Генерация пакета NuGet из файла NuSpec 394
Структура пакета NuGet 395
Папка Content 395
Папка libs 396
Папка tools 397
Типы пакетов NuGet 397
Пакеты сборок 398
Пакеты инструментов 398
Метапакеты 398
Разделение пакетов NuGet 398
Публикация в открытом репозитории пакетов NuGet.org 398
Размещение собственного репозитория пакетов 399
Советы, трюки и ловушки 402
Ловушка: NuGet не решает проблемы "ада DLL” 402
Совет: используйте команду Install-Package -Version для установки специфичной версии пакета 403

Совет: используйте систему Semantic Versioning 404
Совет: помечайте "бета-пакеты” с помощью маркеров предварительной версии 404
Ловушка: избегайте указания "строгих” зависимостей от версий в файлах NuSpec 405
Совет: используйте специальные репозитории для управления версиями пакетов 406
Совет: сконфигурируйте свои построения непрерывной интеграции для генерации пакетов NuGet 407
Резюме 408
Приложение В. Рекомендуемые приемы 409
Используйте для управления зависимостями диспетчер пакетов NuGet 409
Полагайтесь на абстракции 409
Избегайте использования ключевого слова new 409
Избегайте прямых ссылок на объект HttpContext (используйте HttpContextBase) 410
Избегайте "магических строк” 410
Отдавайте предпочтение моделям перед словарем ViewData 410
Не записывайте HTML-разметку в "серверный” код 411
Не выполняйте бизнес-логику в представлениях 411
Консолидируйте часто используемые фрагменты представлений с помощью вспомогательных методов 411
Отдавайте предпочтение презентационным моделям перед прямым использованием бизнес объектов 411
Инкапсулируйте операторы if во вспомогательные методы HTML в представлениях 411
Отдавайте предпочтение явным именам представлений 412
Отдавайте предпочтение объектам параметров перед длинными списками параметров 413
Инкапсулируйте разделяемую/общую функциональность, логику и данные с помощью фильтров действий или дочерних действий (Html.RenderAction) 414
Отдавайте предпочтение группированию действий в контроллеры на основе того, как они связаны с бизнес-концепциями 415
Избегайте группирования действий в контроллеры на основе технических отношений 415
Отдавайте предпочтение фильтрам действий на самом высоком подходящем уровне 415
Отдавайте предпочтение нескольким представлениям (и/или частичным представлениям) перед сложной логикой if-then-else, которая отображает и скрывает разделы 415
Отдавайте предпочтение шаблону Post-Redirect-Get при отправке данных формы 416
Отдавайте предпочтение задачам запуска перед логикой, размещаемой в методе Application_Start() (Global.asax) 417
Отдавайте предпочтение атрибуту авторизации перед императивными проверками безопасности 417
Отдавайте предпочтение использованию атрибута маршрута перед более обобщенными глобальными маршрутами 418
Подумайте об использовании маркера противодействия атакам CSRF 418
Подумайте об использовании атрибута AcceptVerbs для ограничения способов вызова действий 418
Подумайте об использовании кеширования вывода 419
Подумайте об удалении неиспользуемых механизмов представлений 419
Подумайте об использовании специальных результатов действий в уникальных сценариях 420
Подумайте об использовании асинхронных контроллеров для задач, которые могут выполняться параллельно 420
Приложение Г. Перекрестные ссылки: целевые темы, функциональные возможности и сценарии 421
Предметный указатель 424

Глава из книги

ГЛАВА 3
Работа с данными.
Редко когда удается встретить приложение, которое бы каким-либо образом не имело дело с данными, поэтому не должен вызывать удивление тот факт, что ASP.NET MVC предоставляет великолепную поддержку работы с данными на всех уровнях инфраструктуры. В этой главе мы рассмотрим инструменты, обеспечивающие такую поддержку, и покажем, как использовать их в сценариях, управляемых данными, за счет добавления этой функциональности к образцовому приложению ЕВиу.
Поскольку ЕВиу является сайтом онлайновых аукционов, наиболее важным его сценарием является предоставление пользователям возможности создавать списки аукционных товаров, содержащие детальные сведения по каждому элементу, который они желают продать. Итак, давайте посмотрим, как ASP.NET MVC может помочь в поддержке этого важного сценария.

Построение формы.
Концепция HTML-формы столь же стара, как сама веб-сеть. Хотя в настоящее время браузеры стали более функциональными, а HTML-форму можно стилизовать и снабдить поведением с помощью JavaScript таким способом, который еще пять лет назад казался невозможным, в ее основе по-прежнему лежит набор старых добрых полей, готовых к заполнению и обратной отправке серверу.
Несмотря на то что ASP.NET MVC приветствует написание большей части HTML-разметки "вручную”, эта инфраструктура предлагает набор вспомогательных методов HTML, позволяющих генерировать разметку для HTML-форм, среди которых Html. TextBox, Html. Password и Html. HiddenField. Кроме того, в ASP.NET MVC имеется несколько "более интеллектуальных” вспомогательных методов, таких как Html. LabelFor и Html. EditorFor, которые динамически определяют подходящую HTML-разметку на основе имени и типа переданного свойства модели.
Именно эти вспомогательные методы будут использоваться в примере веб-сайта ЕВиу для построения HTML-формы, которая позволит пользователям выполнять отправку в адрес действия AuctionsController. Create для создания новых аукционных товаров. Чтобы увидеть данные вспомогательные методы в действии, добавьте новое представление по имени Create. cshtml и заполните его следующей разметкой:

6using (Html.BeginForm()) {

@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)

<р>
0Html.LabelFor(model => model.Description)
0Html.EditorFor(model => model.Description)

0Html.LabelFor(model => model.StartPrice)
0Html.EditorFor(model => model.StartPrice)

0Html.LabelFor(model => model.EndTime)
0Html.EditorFor(model => model.EndTime)

}
Затем добавьте в контроллер приведенное ниже действие для визуализации этого представления:
[HttpGet] public ActionResult Create()
{
return View();
}
Показанное выше представление визуализируется в следующую HTML-разметку, предназначенную для браузера:
Пользователь затем заполняет эту форму значениями и отправляет ее действию /auctions/create. Хотя с точки зрения браузера это выглядит как отправка формы самой себе (URL для визуализации формы изначально установлен также в /auctions/create), именно здесь в игру вступает второе действие контроллера Create с атрибутом HttpPostAttribute, сообщающее ASP.NET MVC о том, что это перегруженная версия, которая обрабатывает действие POST отправки формы.
А теперь самое время действительно сделать что-нибудь со значениями отправленной формы — но что?

Обработка отправок формы.
Перед тем как можно будет работать со значениями, отправленными контроллеру, понадобится извлечь их из запроса. Как было показано в разделе "Параметры действий” главы 1, простейший способ предусматривает использование модели в качестве параметра действия и, к счастью, модель уже создана: вспомните класс Auction из раздела "Модели” в главе 1.
Для привязки к созданному ранее классу Auction просто укажите параметр типа Auction в числе параметров действия контроллера Create:
[HttpPost] public ActionResult Create(Auction auction)
{
// Создать объект Auction в базе данных, return View(auction);
}
Наиболее важным аспектом модели Auction в этой точке является тот факт, что имена свойств (Title, Description и т.д.) соответствуют именам полей формы, которые были отправлены действию Create. Имена свойств критически важны, поскольку привязка модели ASP.NET MVC пытается заполнить их значения из полей формы с совпадающими именами.
Если вы запустите это приложение, выполните отправку почты и обновите страницу, то увидите, что модель Auction заполнилась введенными ранее значениями. В этот момент действие просто возвращает заполненный параметр auction обратно представлению, которое может применяться для отображения значений формы пользователю с целью подтверждения отправки.
Это приближает нас на один шаг к получению приложения, которое делает что-то полезное, однако по-прежнему остается много чего нужно реализовать, начиная с действительного сохранения данных.

Сохранение данных в базе.
Хотя инфраструктура ASP.NET MVC Framework не имеет никаких встроенных средств доступа к данным, существует множество популярных библиотек .NET доступа к данным, которые помогут упростить работу с базой данных.

Одной из таких библиотек является Entity Framework (EF) от Microsoft. Библиотека Entity Framework — это простая и гибкая инфраструктура объектно-реляционного отображения (object relational mapping — ORM), которая позволяет разработчикам запрашивать и обновлять данные в базе объектно-ориентированным путем. Более того, Entity Framework в действительности представляет собой часть платформы .NET Framework, с полной поддержкой и обилием доступной документации, обеспечиваемой Microsoft.

Инфраструктура Entity Framework предлагает несколько разных подходов к определению модели данных и применению этой модели для доступа в базу данных, но, пожалуй, самым интригующим подходом является Code First ("сначала код”). Образ мышления, заложенный в разработку Code First, заключается в том, что модель приложения является центральной частью и движущей силой всего происходящего во время разработки.
Entity Framework Code First: соглашение по конфигурации
При разработке Code First взаимодействие осуществляется через простые классы моделей (также называемые традиционными объектами CLR (Plain Old CLR Object — POCO)). Подход Code First в Entity Framework заходит настолько далеко, что даже генерирует на основе модели схему базы данных и использует эту схему для создания базы данных и ее сущностей (таблиц, отношений и т.д.) при запуске приложения.

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

Например, в образцовом приложении EBuy класс Auction отображается на таблицу базы данных Auctions и все его свойства представляют столбцы в этой таблице. Имена таблицы и столбцов автоматически выводятся из имен класса и его членов.

Показанная ранее модель Auction очень проста, но с ростом потребностей приложения сложность модели также будет возрастать: мы добавим дополнительные свойства, бизнес-логику и даже отношения с другими моделями. Однако это не проблема для Entity Framework Code First, поскольку это средство обычно способно обрабатывать более сложные модели с той же легкостью, что и простые. В главе 8 в простую модель Auction, показанную в этой главе, привносится более реалистичная сложность и демонстрируется, что подход Entity Framework Code First обладает возможностью обработки этих более сложных отображений (а также объясняется, что делать, если он не справляется с ними).

Создание уровня доступа к данным с помощью Entity Framework Code First.
В основе подхода Entity Framework Code First лежит класс System. Data. Entity. DbContext. Этот класс (или созданные производные от него классы) выступает в качестве шлюза к базе данных, предоставляя все необходимые действия, связанные с данными. Чтобы приступить к использованию класса DbContext, понадобится создать собственный класс, производный от него, который на самом деле довольно прост:
using System.Data.Entity;
public class EbuyDataContext : DbContext
{
public DbSet Auctions { get; set; }
}
В этом примере (EbuyDataContext.es) мы создали специальный класс контекста данных по имени EbuyDataContext, производный от DbContext. Этот отдельный класс определяет свойство System. Data . Entity. DbSet, где T — это сущность, которая будет редактироваться и сохраняться в базе данных. В предшествующем примере мы определили System. Data. Entity. DbSet для указания на то, что приложение нуждается в сохранении и редактировании экземпляров класса Auction в базе данных. Однако в контексте данных можно определять более одной сущности, и по мере продвижения разработки мы будем добавлять в класс EbuyDataContext дополнительные сущности (или свойства DbSet).

Если создание специального контекста данных осуществляется легко, то его использование еще легче, как продемонстрировано в следующем примере. В следующем фрагменте действие контроллера Create модифицируется для сохранения отправленного объекта Auction в базе данных, для чего объект Auction просто добавляется в коллекцию EbuyDataContext.Auctions с последующим сохранением изменений:
[HttpPost] public ActionResult Create(Auction auction)
{
var db = new EbuyDataContext () ; db.Auctions.Add(auction); db.SaveChanges(); return View(auction);
}
На этот раз после запуска приложения и отправки заполненной формы в таблице Auctions базы данных появится новая строка, содержащая информацию, отравленную в форме.
Если вы продолжите пользоваться этим примером и поэкспериментируете с разными значениями в полях формы, то заметите, что привязка моделей ASP.NET MVC является весьма терпимой к ошибкам, позволяя вводить все что угодно и молча отказывая, когда она не может преобразовать отправленные значения формы в строгие типы (например, в ситуации, когда пользователь вводит строку ABC в поле типа int). Если необходим более строгий контроль над тем, какие данные сохраняются в базе, потребуется применять проверку достоверности данных к модели.

Проверка достоверности данных.
Что касается данных, то обычно существует ряд применяемых правил и ограничений, таких как поля, которые не должны быть пустыми или значения которых должны находиться в заданном диапазоне, чтобы рассматриваться как "допустимые”. Естественно, ASP.NET MVC распознает такие важные концепции, интегрируя их прямо в процесс обработки каждого запроса.
В качестве части процесса выполнения действия контроллера инфраструктура ASP.NET MVC Framework проверяет достоверность данных, которые передаются этому действию контроллера, заполняя объект ModelState любыми обнаруженными ошибками и передавая этот объект контроллеру. Затем действия контроллера могут запросить ModelState для выяснения допустимости запроса и отреагировать соответствующим образом, например, сохранить допустимый объект в базе данных или вернуть пользователя в исходную форму для исправления ошибок проверки достоверности, зафиксированных в недопустимом запросе.
Ниже приведен пример действия AuctionsController. Create, обновленного для проверки словаря ModelState с применением только что описанной логики "сохранить или исправить”:
[HttpPost] public ActionResult Create(Auction auction)
{
if (ModelState.IsValid)
{
var db = new EbuyDataContext () ; db.Auctions.Add(auction); db.SaveChanges();
return RedirectToAction("Index");
}
return View(auction);
}
Инфраструктура ASP.NET MVC Framework — это не единственное средство, которое может добавлять ошибки проверки достоверности в ModelState. Разработчики могут запускать собственную логику для обнаружения проблем, которые инфраструктура не может перехватить, и вручную добавлять информацию об ошибках, используя такой метод:
ModelState.AddModelError(string key, string message)
Пусть, например, существует требование, что аукционы должны длиться, по крайней мере, один день. Другими словами, значение свойства EndTime объекта Auction должно превышать текущее время плюс один день.
Действие AuctionsController. Create может явно проверять это перед тем, как пытаться сохранить объект Auction, и предусматривать специальное сообщение об ошибке, когда такая ситуация возникает:
[HttpPost] public ActionResult Create(Auction auction)
{
if (auction.EndTime <= DateTime.Now.AddDays(1) )
{
ModelState.AddModelError(
"EndTime",
"Auction must be at least one day long"
);
}
if (ModelState.IsValid)
{
var db = new EbuyDataContext () ;
db .Auctions .Add (auction) ;
db.SaveChanges ();
return RedirectToAction("Index");
}
return View(auction);
}
Хотя этот подход работает довольно неплохо, он приводит к нарушению принципа разделения ответственности в приложении. В частности, контроллеры не должны содержать бизнес-логику подобного рода: бизнес-логика относится к модели. Итак, давайте перенесем бизнес-логику в модель.

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

На самом деле это настолько общая потребность, что в рамках ядра .NET Framework поставляется очень эффективный и простой в использовании API-интерфейс проверки достоверности данных под названием Data Annotations (аннотации данных). Как должно быть понятно из названия, API-интерфейс Data Annotations предоставляет набор атрибутов .NET, которые разработчики могут применять к свойствам классов объектов данных. Эти атрибуты предлагают декларативный способ применения правил проверки достоверности непосредственно к модели.

Более того, привязка модели ASP.NET MVC обеспечивает поддержку аннотаций данных без дополнительного конфигурирования. Для демонстрации работы аннотаций данных ASP.NET MVC давайте рассмотрим процесс применения проверки достоверности к классу Auction. Перед началом применения логики проверки достоверности необходимо определить, какие значения ожидаются для свойств класса Auction. Какие поля должны быть обязательными? Имеют ли какие-то поля определенные диапазоны допустимых значений?

Обязательные поля.
Поскольку свойства Title и Description класса Auction критически важны для описания продаваемого на аукционе товара, мы применим к этим двум полям аннотацию данных RequiredAttribute, пометив их как поля, которые обязательно должны иметь данные, чтобы считаться допустимыми:
[Required] public string Title { get; set; }
[Required] public string Description { get; set; }
В дополнение к пометке поля как обязательного с помощью RequiredAttribute, можно также обеспечить, чтобы строковые значения имели максимальную длину, применив для этого атрибут StringLengthAttribute. Например, было решено, что заголовки аукционных товаров должны сохраняться короткими, не превышая максимальную длину в 50 символов:
[Required, StringLength(50)] public string Title { get; set; }
Если теперь пользователь отправит форму с полем Title, в котором введена строка с более чем 50 символами, средство проверки достоверности модели ASP.NET MVC сообщит об ошибке.

Допустимые диапазоны.
Далее рассмотрим стартовую цену аукционного товара: она представляется свойством StartPrice, имеющим тип decimal. Поскольку decimal — это тип значения, свойство StartPrice будет всегда иметь, по крайней мере, значение по умолчанию, равное 0, так что пометка этого свойства как обязательного будет избыточной. Тем не менее, со стартовыми ценами аукционных товаров связана другая логика: эти значения не могут быть отрицательными! Чтобы решить эту проблему, примените атрибут RangeAttribute к полю StartPrice и укажите в качестве минимального значения 1. Так как RangeAttribute требует еще и максимального значения, укажите также верхний предел.
[Range(1, 10000] public decimal StartPrice { get; set; }
В этом примере используется диапазон типа double, но аннотация RangeAttribute имеет также перегруженную версию (Range (Type type, string min, string max)) для поддержки диапазона любого типа, который реализует интерфейс IComparable и может быть создан путем разбора или преобразования строковых значений. Хорошим примером может служить проверка диапазона дат; например, следующая аннотация гарантирует, что дата находится после определенного момента времени:
[Range(typeof(DateTime ), "1/1/2012", "12/31/9999"] public DateTime EndTime { get; set; }
Этот пример обеспечивает, что значение свойства EndTime будет, по крайней мере, позже 1 января 2012 г.

Параметрами атрибутов .NET должны быть значения, которые известны на этапе компиляции и не могут вычисляться во время выполнения, что «р* исключает использование таких значений, как DateTime.Now, для выяснения, относится ли дата к будущему. Вместо этого мы должны выбрать произвольную дату, например, 1/1/2012, которая хотя и не обеспечит того, что введенная дата относится к моменту, находящемуся после отправки формы, но, по крайней мере, позволит избежать ввода дат из далекого прошлого.

Этот недостаток точности является компромиссом, на который пришлось пойти, чтобы иметь возможность пользоваться RangeAttribute. Если сложившаяся ситуация требует большей точности, придется прибегнуть к атрибуту СиstomValidationAttribute, который позволяет выполнять произвольную логику для проверки достоверности свойств. Хотя возможность выполнения произвольного кода посредством СиstomValidatorAttribute, несомненно, удобна, она представляет собой менее декларативный подход, который ограничивает информацию, доступную другим компонентам, таким как инфраструктура проверки достоверности на стороне клиента ASP.NET MVC.

Специальные сообщения об ошибках.
В заключение важно отметить, что все аннотации данных предоставляют свойство ErrorMessage, которое можно использовать для указания сообщения об ошибке, отображаемого пользователю вместо стандартного сообщения об ошибке от API-интерфейса Data Annotations. Укажите желаемое значение для этого свойства в каждой аннотации данных, добавленной к модели.

Финальный класс, включающий все аннотации данных, которые обсуждались в этом разделе, должен выглядеть примерно так:
public class Auction {
[Required] [StringLength(50,
ErrorMessage = "Title cannot be longer than 50 characters")] public string Title { get; set; }
[Required] public string Description { get; set; }
[Range(1, 10000,
ErrorMessage = "The auction's starting price must be at least 1")] public decimal StartPrice { get; set; }
public decimal CurrentPrice { get; set; } public DateTime EndTime { get; set; }
}
Теперь, когда в модели определена вся необходимая логика проверки достоверности, давайте вернемся к контроллеру и представлению и посмотрим, как отображать сообщения об ошибках проверки пользователю.

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

Проблема в том, что хотя представление Create может показывать поля с недопустимыми значениями в красной рамке, она все еще не отображает сообщения об ошибках, указывающие конкретные причины их возникновения. Давайте позаботимся об этом.
В качестве напоминания ниже приведена текущая разметка для свойства Title:
<р>
@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
QViewData.ModelState["Title"]

Нам необходимо добавить к этой разметке еще одну строку для отображения любых сообщений, связанных с проверкой достоверности свойства Title. Простейший способ выяснить, возникли ли ошибки проверки свойства Title, заключается в обращении к ModelState напрямую — ViewData.ModelState ["Title"] возвращает объект, содержащий коллекцию ошибок, которые относятся к свойству Title.
Затем можно пройти в цикле по этой коллекции, чтобы визуализировать сообщения об ошибках на странице:
<р>
@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
6foreach(var error in ViewData.ModelState["Title"] .Errors)
{
6error.ErrorMessagec/span>
}

Хотя это работает довольно хорошо, ASP.NET MVC предлагает даже лучший подход к визуализации всех ошибок для заданного свойства: вспомогательный метод Html. ValidationMessage (string modelName).

Вспомогательный метод Html. ValidationMessage () позволяет заменить весь показанный выше цикл f oreach единственным вызовом метода и получить тот же самый результат:
0Html.ValidationMessageFor(model => model.Title)

Добавьте вызов Html .ValidationMessage () для каждого свойства в модели. Инфраструктура ASP.NET MVC теперь будет визуализировать все проблемы, возникающие во время проверки достоверности, прямо рядом с полями формы, к которым проверка применяется.
В дополнение к вспомогательному методу Html .ValidationMessage () уровня свойств, ASP.NET MVC также предоставляет вспомогательный метод Html .Validation Summary () . Этот метод позволяет визуализировать все ошибки проверки достоверности для формы в одном месте (например, в верхней части формы), предоставляя пользователю сводку по всем проблемам, которые должны быть устранены, чтобы форма могла быть успешно отправлена.

Вспомогательный метод Html. ValidationSuimnary () очень легко использовать — нужно просто вызвать Html. ValidationSuimnary () там, где должна располагаться сводка:
6using (Html.BeginForm())
{
@Html.ValidationSummary()

@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)

}
Если теперь отправить недопустимые значения в полях формы, вы увидите сообщения об ошибках в двух местах (рис. 3.1): в сводке по проверке достоверности (благодаря вызову Html .ValidationSuimnary ()) и рядом с полями (благодаря вызову Html .ValidationMessage ()).
Если вы хотите избежать отображения дублированных сообщений об ошибках, можете модифицировать вызовы Html .ValidationMessage () и указать короткое специальное сообщение, такое как единственный символ звездочки:
<р>
@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title, "*")
Ниже приведена полная разметка для представления Create после добавления всех средств проверки достоверности:

6using (Html.BeginForm())
{
@Html.ValidationSummary()

@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title, "*" )

@Html.LabelFor(model => model.Description)
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description, "*")

@Html.LabelFor(model => model.StartPrice)
@Html.EditorFor(model => model.StartPrice)
@Html.ValidationMessageFor(model => model.StartPrice)

6Html.LabelFor(model => model.EndTime)
@Html.EditorFor(model => model.EndTime)
@Html.ValidationMessageFor(model => model.EndTime)

}
Вся проверка достоверности, продемонстрированная до сих пор, производилась на стороне сервера, требуя полного цикла обмена между браузером и сервером для обработки каждого потенциально недопустимого запроса и выдачи в качестве ответа полностью визуализированного представления.
Хотя этот подход работает, он определенно не оптимален. В главе 4 показано, как реализовать проверку достоверности на стороне клиента, чтобы усовершенствовать этот подход и выполнять большинство (если не все) проверок прямо в браузере. Это позволит избежать дополнительных запросов к серверу, сохраняя как полосу пропускания, так и серверные ресурсы.

Резюме.
В этой главе речь шла об использовании подхода Entity Framework Code First для создания и обслуживания базы данных приложения. Вы увидели, насколько легко с помощью Entity Framework устанавливать базу данных: это сводится к всего лишь нескольким строкам кода и не требует предварительного рисования диаграммы со схемой или написания SQL-запросов для моделирования и создания базы данных. Было показано, что Entity Framework работает за счет следования соглашениям, а также описаны некоторые базовые соглашения. Также кратко рассматривалось применение средства привязки моделей ASP.NET MVC для автоматического заполнения объектов состоянии.

Рекомендуемая техническая литература:
1.Введение в .NET 4.0 и Visual Studio 2010 для профессионалов. Алекс Макки
2.ASP.NET MVC 3 Framework с примерами на C# для профессионалов, 3-е издание, Адам Фримен, Стивен Сандерсон.
3.C# 5.0. Карманный справочник, Джозеф Албахари, Бен Албахари.
4.Язык программирования C# 5.0 и платформа .NET 4.5, 6-е издание, Эндрю Троелсен.
5.C# 5.0. Справочник. Полное описание языка, Джозеф Албахари, Бен Албахари
6.Jess Chadwick, Todd Snyder "Programming ASP.NET MVC 4: Developing Real-World Web Applications with ASP.NET MVC" O'Reilly, 2012 год, 472 стр.

 

Скачать книгу бесплатно64,2 мб. pdf