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

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

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

возвращает true или false. Этот метод мы используем чаще всего при создании сложных mockобъектов.

Использование mock-объектов для диапазона значений

Наше последнее использование объекта It связано с методом IsInRange, который позволяет нам охватить определенный диапазон значений параметров:

mock.Setup(m => m.ApplyDiscount(It.IsInRange<decimal>(10, 100, Range.Inclusive)))

.Returns<decimal>(total => total - 5);

Мы включили это для полноты картины, но в наших собственных проектах, мы, как правило, используем метод Is и предикат, который делает вот это:

mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v >= 10 && v <= 100)))

.Returns<decimal>(total => total - 5);

Результат тот же, но мы считаем, что работа через предикат отличается более гибким подходом. Moq обладает целым рядом чрезвычайно полезных функций, и вы можете ознакомиться с ними на http://code.google.com/p/moq/wiki/QuickStart.

Резюме

В этой главе мы рассмотрели три инструмента, которые являются важными для эффективной MVC разработки: Ninject, встроенная поддержка Visual Studio для модульного тестирования и Moq. Есть много альтернатив, как с открытым кодом, так и коммерческих, для всех этих трех инструментов, и вы не будете обделены, если вам не понравятся те инструменты, которые используем мы.

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

151

SportsStore: реальное приложение

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

Наше приложение – SportsStore - мы создадим по классической схеме, которая применяется во всех интернет-магазинах. Мы создадим онлайн-каталог товаров, который можно просматривать по категориям и страницам, корзину, где можно добавлять и удалять товары, и кассу, где можно ввести информацию о доставке. Мы также создадим область администрирования, в которой будут возможности для управления каталогом - создать, прочитать, обновить и удалить (create, read, update и delete - CRUD), для которой установим защиту, чтобы только залогинившиеся администраторы могли вносить изменения.

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

Модульное тестирование

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

Но мы знаем, что не все разделяют это убеждение. Если вы не хотите проводить модульное тестирование - мы не против. Таким образом, всю информацию о модульном тестировании или TDD мы будем выносить в специальные блоки, такие как этот. Если вас не интересует модульное тестирование, вы можете пропустить эти секции, и приложение SportsStore от этого не пострадает. Вы сможете воспользоваться всеми техническими преимуществами ASP.NET MVC и без модульного тестирования, но, тем не менее, оно поможет вам воспользоваться преимуществами MVC и в разработке, и в тестировании.

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

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

152

Начинаем

Если вы планируете кодировать SportsStore на своем компьютере, вам нужно будет установить программное обеспечение, описанное в главе 2. Вы также можете взять SportsStore из архива с кодом, который прилагается к этой книге (доступен на apress.com). Мы включали скриншоты проекта после того, как добавляли основные функции, чтобы можно было проследить, как изменялось приложение в процессе нашей работы над ним.

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

Создаем решение и проекты Visual Studio

Мы собираемся создать решение Visual Studio, которое включает три проекта. Один проект будет содержать нашу доменную модель, второй будет нашим MVC-приложением, а третий будет содержать модульные тесты. Для начала мы создадим новое решение Visual Studio под названием SportsStore, используя шаблон Blank Solution, который можно найти в разделе Other Project Types диалога New Project, как показано на рисунке 7-1. Для создания решения нажмите ОК.

Рисунок 7-1: Создаем новое решение Visual Studio

Решение Visual Studio является контейнером для одного или нескольких проектов. Для нашего примера нам понадобится три проекта, которые мы описали в таблице 7-1. Чтобы добавить проект, щелкните правой кнопкой по вкладке Solution в Solution Explorer и выберите Add New Project в контекстном меню.

153

Таблица 7-1: Три проекта SportsStore

Название проекта

 

Шаблон проекта

 

 

Class Library

SportsStore.Domain

 

 

 

 

ASP.NET MVC 4 Web

Application (Выберите Basic,

SportsStore.WebUI

когда будет предложено

выбрать шаблон проекта)

SportsStore.UnitTests Unit Test Project

Цель проекта

Содержит доменные объекты и логику;

поддерживает механизм хранения с

помощью хранилища, созданного используя Entity Framework.

Содержит контроллеры и представления; выступает в качестве интерфейса приложения.

Содержит модульные тесты для двух других проектов.

Мы используем версию Basic шаблона ASP.NET MVC 4 Web Application, поскольку она содержит несколько полезных дополнений: набор часто используемых библиотек JavaScript и папку Scripts

том числе JQuery, JQuery UI, Knockout и Modernizr), базовый макет в папке Views и несколько CSS

стилей в папке Content (в том числе стили валидации формы, которые мы добавляли вручную в главе 2).

Примечание

Файлы библиотеки JavaScript, которые включены в шаблон Basic, описаны в главе 24. Стив является создателем библиотеки Knockout. Адам описал Knockout в своей книге Pro JavaScript for Web Apps (Apress, 2012), наряду с Modernizr и некоторыми другими важными библиотеками JavaScript и интерфейсами HTML5. Адам также писал о JQuery, JQuery UI и JQuery Mobile в своей книге Pro JQuery (Apress, 2012). Как вы, возможно, догадываетесь, мы оба испытываем глубокий интерес к этой области и считаем, что невозможно создать хорошее серверное приложение, не уделяя должного внимания клиентской стороне.

Вы можете удалить файл Class1.cs в проекте SportsStore.Domain - мы не будем его использовать. Когда вы это сделаете, окно Solution Explorer должно выглядеть так, как показано на рисунке 7-2.

Рисунок 7-2: Проекты отображаются в окне Solution Explorer

154

Чтобы сделать отладку легче, щелкните правой кнопкой мыши по проекту SportsStore.WebUI и выберите команду Set as Startup Project в контекстном меню (имя выделится полужирным шрифтом). Это означает, что, когда вы выберите Start Debugging или Start without Debugging в меню Debug, будет запущен именно этот проект.

Добавляем ссылки

Нам нужно добавить ссылки на библиотеки инструментов, которые мы собираемся использовать для двух проектов. Самый простой способ добавить библиотеки - это кликнуть правой кнопкой мыши по каждому проекту, выбрать пункт Manage NuGet Packages, который вызывает диалог NuGet, и найти и установить необходимые библиотеки.

Нам также нужно установить зависимости между проектами. Кликните правой кнопкой мыши по каждому проекту в окне Solution Explorer, выберите пункт Add Reference и добавьте ссылки на необходимые библиотеки инструментов или другие проекты из раздела Solution.

Вы можете просмотреть подробную информацию о проектах, пакетах NuGet и зависимостях от других проектов в таблице 7-2.

Внимание

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

Таблица 7-2: Необходимые зависимости проекта

 

 

 

 

 

 

Название проекта

 

Зависимости

Зависимости

Ссылки Microsoft

 

инструментов

проектов

 

 

 

 

 

 

 

 

SportsStore.Domain

 

None

None

System.Web.Mvc

 

System.ComponentModel.DataAnnotations

 

 

 

 

 

 

 

 

 

SportsStore.WebUI

 

Ninject, Moq

SportsStore.Domain

None

 

 

 

 

 

SportsStore.UnitTests

 

Ninject, Moq

SportsStore.Domain,

System.Web.Mvc, System.Web,

 

SportsStore.WebUI

Microsoft.CSharp

 

 

 

Чтобы добавить ссылку на сборку System.Web.Mvc в проект SportsStore.UnitTests, кликните правой кнопкой мыши по имени проекта в Solution Explorer, выберите пункт Add Reference и перейдите в раздел Assemblies - Extensions. Вы найдете несколько ссылок на сборки под названием System.Web.Mvc. Убедитесь, что вы добавляете версию 4.0.0. Если вы выберите более раннюю версию, у вас могут возникнуть проблемы при тестировании функций, которые были изменены в последней версии. Другие ссылки Microsoft добавляются таким же образом, но находятся в разделе

Assemblies - Framework.

Устанавливаем контейнер DI

В главе 6 мы разобрали, как с помощью Ninject создать пользовательский DR (dependency resolver), который используется платформой MVC для создания экземпляров объектов в приложении. В этом примере мы собираемся поступить иначе, а именно - создать пользовательскую фабрику контроллеров. Это проиллюстрирует одну из многих точек расширения MVC Framework, где вы можете добавить пользовательский код, чтобы изменить поведение платформы или, как мы делаем здесь, ограничить DI и использовать его только в одной части приложения. Обычно DR используется для работы с DI и пользовательской фабрикой контроллеров, чтобы изменить то, как размещаются

155

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

Создайте в проекте SportsStore.WebUI новую папку под названием Infrastructure, а затем создайте класс NinjectControllerFactory и отредактируйте файл класса так, чтобы он соответствовал листингу 7-1.

Листинг 7-1: Класс NinjectControllerFactory

using System;

using System.Web.Mvc; using System.Web.Routing; using Ninject;

namespace SportsStore.WebUI.Infrastructure

{

//реализация пользовательской фабрики контроллеров,

//наследуясь от фабрики используемой по умолчанию

public class NinjectControllerFactory : DefaultControllerFactory

{

private IKernel ninjectKernel; public NinjectControllerFactory()

{

// создание контейнера

ninjectKernel = new StandardKernel(); AddBindings();

}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)

{

//получение объекта контроллера из контейнера

//используя его тип

return controllerType == null ? null

: (IController)ninjectKernel.Get(controllerType);

}

private void AddBindings()

{

// конфигурирование контейнера

}

}

}

Мы еще не добавляли привязки Ninject, но когда они нам понадобятся, мы можем сделать это с помощью метода AddBindings. Необходимо сообщить MVC, что для создания объектов контроллера мы хотим использовать класс NinjectController. Для этого мы делаем дополнение в методе

Application_Start в файле Global.asax.cs проекта SportsStore.WebUI, которое выделено жирным шрифтом в листинге 7-2.

Листинг 7-2: Регистрируем NinjectControllerFactory в MVC Framework

using SportsStore.WebUI.Infrastructure; using System.Web.Http;

using System.Web.Mvc;

using System.Web.Optimization; using System.Web.Routing;

namespace SportsStore.WebUI

{

public class MvcApplication : System.Web.HttpApplication

156

{

protected void Application_Start()

{

AreaRegistration.RegisterAllAreas();

WebApiConfig.Register(GlobalConfiguration.Configuration);

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

RouteConfig.RegisterRoutes(RouteTable.Routes);

BundleConfig.RegisterBundles(BundleTable.Bundles);

ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

}

}

}

Запускаем приложение

Если вы выберите команду Start Debugging в меню Debug, то получите страницу с сообщением об ошибке. Это произойдет потому, что вы запросили URL, связанный с контроллером, для которого у Ninject нет привязки, что показано на рисунке 7-3.

Рисунок 7-3: Страница с сообщением об ошибке

Если вы видите это окно, то Visual Studio 2012 и ASP.NET MVC у вас настроены и работают правильно. Вы можете остановить отладку, закрыв окно браузера, если ваш браузер по умолчанию – Internet Explorer. Если нет, то переключитесь в Visual Studio и выберите пункт Stop Debugging в

меню Debug.

Упрощеная отладка

Когда вы запускаете проект из меню Debug, Visual Studio откроет новое окно браузера для отображения приложения. Чтобы ускорить процесс, вы можете открыть приложение в отдельном окне. Если вы уже запускали отладчик хотя бы один раз, кликните правой кнопкой мыши ярлык IIS

157

Express в системном трее и выберите URL для вашего приложения в контекстном меню, как показано на следующем скриншоте.

Таким образом, вам не нужно будет запускать новую сессию отладки после каждого изменения, чтобы увидеть эффект. Вы просто компилируете решение в Visual Studio, нажав F6 или выбрав Build - Build Solution, а затем перезагружаете страницу в браузере.

Создаем доменную модель

Мы начнем с создания доменной модели. Практически все в приложении MVC вращается вокруг доменной модели, так что она и будет нашей стартовой площадкой.

Поскольку мы разрабатываем приложение для электронной коммерции, очевидно, самой главной доменной сущностью у нас будет товар. Создайте новую папку под названием Entities в проекте SportsStore.Domain, а в ней - новый класс C# под названием Product. Искомая структура показана на рисунке 7-4.

Рисунок 7-4: Создаем класс Product

158

Содержание класса Product вам уже известно, так как мы собираемся использовать тот же класс, что и в предыдущих главах. Отредактируйте файл класса Product, чтобы он соответствовал листингу 7- 3.

Листинг 7-3: Файл класса Product

namespace SportsStore.Domain.Entities

{

public class Product

{

public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; }

}

}

Мы следуем соглашению, по которому мы определяем доменную модель в отдельном проекте Visual Studio, а это означает, что класс должен быть помечен как public. Следовать этому соглашению не обязательно, но мы считаем, что оно помогает сохранять изоляцию модели от контроллеров.

Создаем абстрактное хранилище

Разумеется, нам нужно каким-то образом получать объекты Product из базы данных. Как мы уже объяснили в главе 3, мы хотим держать логику хранения изолированно от объектов доменной модели, и для этого мы будем использовать шаблон хранилища. Сейчас мы не будем думать о том, как мы собираемся реализовать хранение, и начнем с того, что определим для него интерфейс.

Создайте новую папку верхнего уровня в проекте SportsStore.Domain под названием Abstract и новый интерфейс под названием IProductsRepository, содержание которого показано в листинге 7- 4. Чтобы добавить новый интерфейс, кликните правой кнопкой мыши папку Abstract, выберите Add

- New Item и шаблон Interface.

Листинг 7-4: Файл интерфейса IProductRepository

using System.Linq;

using SportsStore.Domain.Entities;

namespace SportsStore.Domain.Abstract

{

public interface IProductRepository

{

IQueryable<Product> Products { get; }

}

}

Здесь используется интерфейс IQueryable<T>, который позволяет получить последовательность объектов Product и не требует указаний на то, как и где хранятся данные или как следует их извлекать. Класс, который использует интерфейс IProductRepository, может получить объекты Product, не зная того, где они содержатся или каким образом будут ему поставлены. Это и есть суть шаблона хранилища. Мы будем возвращаться к этому интерфейсу на протяжении всего процесса разработки, чтобы добавлять новые функции.

Создаем имитированное хранилище

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

159

собираемся создать имитированную реализацию интерфейса IProductRepository. Это мы сделаем в методе AddBindings класса NinjectControllerFactory в проекте SportsStore.WebUI, как показано в листинге 7-5.

Листинг 7-5: Добавляем имитированную реализацию IProductRepository

using System;

using System.Web.Mvc; using System.Web.Routing; using Ninject;

using SportsStore.Domain.Entities; using SportsStore.Domain.Abstract; using System.Collections.Generic; using System.Linq;

using Moq;

namespace SportsStore.WebUI.Infrastructure

{

public class NinjectControllerFactory : DefaultControllerFactory

{

private IKernel ninjectKernel; public NinjectControllerFactory()

{

ninjectKernel = new StandardKernel(); AddBindings();

}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)

{

return controllerType == null ? null

: (IController)ninjectKernel.Get(controllerType);

}

private void AddBindings()

{

Mock<IProductRepository> mock = new Mock<IProductRepository>(); mock.Setup(m => m.Products).Returns(new List<Product> {

new Product { Name = "Football", Price = 25 }, new Product { Name = "Surf board", Price = 179 }, new Product { Name = "Running shoes", Price = 95 }

}.AsQueryable());

ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);

}

}

}

Для этого дополнения мы должны были добавить в файл несколько пространств имен, но процесс, который создает имитированное хранилище, использует те же самые техники Moq, которые мы рассмотрели в главе 4. AsQueryable является методом расширения LINQ, который преобразует IEnumerable<T> в IQueryable<T>. Это необходимо для соответствия сигнатуры интерфейса.

Мы используем метод ToConstant, потому что хотим, чтобы Ninject возвращал имитацию объекта, когда он получает запрос от реализации интерфейса IProductRepository:

ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);

Вместо того, чтобы каждый раз создавать новый экземпляр реализации объекта, Ninject всегда будет отвечать на запросы интерфейса IProductRepository имитацией объекта.

160

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