Простейший багтрекер на ASP .NET MVC
Рубрика: ASP .NET MVC -> Программирование -> Журнал Хакер -> Статьи
Метки: mvc | примеры | программирование
Просмотров: 17978
Технология ASP .NET уже давно перестала быть неповоротливым монстром и уделом унылых корпоративных приложений. Заряженная MVC фреймворком, платформа ASP .NET превращается в грозное оружие, нацеленное на хайлоад и действительно большие проекты.
Что мне понравилось в ASP .NET MVC
Моя основная работа никогда не была напрямую связана с web-разработкой. Все проекты создавались в свободное время и на технологиях, которые были мне симпатичны. Изначально мой выбор (как и у многих) пал в сторону PHP. Я долго на нем писал код, постоянно закрывая глаза на его странности и проблемы. Мне нравились многие PHP-фреймворки (Kohana, CodeIgniter, FuelPHP и т.д.) и с удовольствием применял их в своих проектах. Однако, несмотря на плюсы и многообразие готовых каркасов мне всегда хотелось переметнуться в другой лагерь и посмотреть, как происходит процесс разработки аналогичных вещей там. Сначала я присматривался к популярному Ruby с его рельсами, но потом все же решил остановиться на .NET. Перечислять плюсы данной платформы можно долго, но наиболее значимыми для меня все же стали:
Если код пахнет откровенной тухлятиной, то компилятор обязательно сообщит об этом и разработчик сможет предпринять необходимые действия. Разрабатывая приложение под ASP .NET я попадаю в ту же самую среду, где в моем распоряжении предсказуемый и строго типизированный язык (C#) с подушкой безопасности в лице компилятора.
Model View Controller
На страницах нашего журнала я уже несколько раз рассматривал Архитектурный паттерн MVC (модель, представление, контроллер). В одном из номеров я даже приводил пример разработки простейшего MVC фреймворка на PHP. Паттерн MVC условно делит архитектуру приложения на три компонента:
Главная цель MVC – обеспечить разделение ответственности между основными компонентами приложения. Контроллер ничего не должен знать о нюансах формирования верстки или хранению данных в БД, а выполнять лишь роль проводника. Пользователь попросил, а контроль нашел правильный путь, не задумываясь о том, что там может произойти.
Багтрекер
Типичный пример при знакомстве с подобными фреймворками – создание еще одного движка для блога. Я сначала хотел пойти тем же путем, но в итоге решил придумать более полезное приложение. Так родилась идея проверить фреймворка на создании проекта «Багтрекер». Такие приложения наиболее востребованы в компаниях, где более-менее налажен процесс разработки и совсем скоро ты убедишься, что сотворить нечто подобное на ASP .NET MVC пуще простого.
Теперь давай определимся с функционалом будущего проекта. Будущий багтрекер должен:
С первыми двумя пунктами все ясно – немного кода на C# и все готово, но как быть с интерфейсом? Мы воспользуемся фреймворком TwitterBootstrap, который позволит нам состряпать симпатичный интерфейс для приложения за несколько минут. Я не стану приводить портянку из пары десятков килобайт HTML кода, а просто дам ссылку на готовую заготовку http://goo.gl/Xvhsmh. Качай и повторяй действия вместе со мной.
Делаем проект
У меня нет профессиональной версии студии, поэтому я воспользовался экспресс версией редакции «Для web». Запускай студию и создавай новый проект «Веб-приложение ASP .NET 4». С гордым названием BugTrackerForX.
В окне мастера создания нового проекта тебе будет предложено выбрать шаблон для приложения. Шаблоны позволяют сразу же снабдить будущее творение определенным функционалом. Например, выбрав шаблон «Интернет-приложение», ты получишь заготовку с готовой регистрационной формой и механизмом аутентификации.
Для своего проекта мы выберем вариант «Простой». Такой прожект не будет включать в себя ничего лишнего и это будет в самый раз для первого знакомства с миром ASP .NET MVC. В окне выбора шаблона для приложения также обрати внимание на пункт «View Engine». Здесь выбирается шаблонизатор для представлений. Тебе доступно два варианта: Razor и ASPX.
Во второй версии фреймворка MVC в качестве шаблонизатора использовался ASPX, пришедший из классического ASP .NET. После PHP’ного многообразия движков для рендеринга представлений начинаешь испытывать приступы тошноты от его неуклюжести. Лучше сразу выбирать Razor, максимально приближенный к аналогичным PHP-решениям (Smarty, Twig и т.д.).
Больше никаких галок ставить не нужно, жми «OK» и студия сгенерирует болванку приложения.
Структура MVC приложения
Открой окно «Solution Explorer» по шире и мотай теорию на ус.
Выжигаем модели
Начинать разработку приложения будем с проектирования моделей. Для создания баг трекера нам потребуются описать несколько моделей:
Каждая модель в ASP .NET MVC фреймворке описывается в виде отдельного класса в папке Model (можно в любом месте). Добавление новые моделей выполняется в контекстном меню с помощью пункта «Add» -> «Class». Создай все выше озвученные модели (см. соответствующие листинги) и возвращайся к тексту статьи.
Листинг 1. Описание модели Category
public class Category
{
Public int CategoryId { get; set; }
public string Title { get; set; }
}
Листинг 2. Описание модели Status
public class StatusModel
{
public int StatusId { get; set; }
public string Title { get; set; }
}
Листинг 3. Описание модели Ticket
public class Ticket
{
public int TicketId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public int? CategoryId { get; set; }
public virtual Category Category { get; set; }
public int? StatusId { get; set; }
public virtual Status Status { get; set; }
public int? UserId { get; set; }
public virtual User User { get; set; }
Листинг 4. Описание модели User
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
Подключаем Entity Framework
Платформа .NET предоставляет нам несколько способов доступа к данным. Мы воспользуемся наиболее актуальным из них – Entity Framework. Помимо типичных задач, возлагающих на ORM (Object relation mapping) фреймворк, в EF реализована поддержка методики CodeFirst, позволяющая девелоперу на этапе разработки не задумываться о дизайне схемы БД. Достаточно лишь описать модели, а вопросы создания базы и таблиц на себя возьмет сам фреймворк. Нельзя сказать, что такая схема будет готова к использованию в продакшене, однако, при разработке приложения с нуля такой подход позволит существенно сэкономить время.
Чтобы воспользоваться плюсами этого фреймворка, нам необходимо подключить его к своему проекту. Проще всего это сделать при помощи консоли управления пакетами NuGet. Сразу после инсталяции Visual Studio for Web расширение Nuget Package Manager недоступно. Его требуется установить самостоятельно, воспользовавшись пунктом «Extensions and Update» в меню «Service». После установки запускай «Nuget Package Manager Console» и вводи в ней команду для установки Entity Framework: «Install-Package EntityFramework».
Установив Entity Framework, мы можем создать контекст данных для наших моделей и начать производить первые манипуляции с добавленными в БД записями. Под страшным словом «контекст» подразумевается создание класса наследника от DbContext (System.Data.Entity), который свяжет модели с таблицами базы данных. Создавай новый класс для контекста данных в директории «Models» и переписывай в него код листинга 5.
Листинг 5. Реализация класса контекста
public class BugTrackerContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Status> Statuses { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<User> Users { get; set; }
}
После создания контекста мы можем воспользоваться технологией CodeFirst, т.е. доверить EntityFramework создание таблиц и полей. В своем примере я воспользуюсь именно этим способом, т.к. статья не резиновая и тратить время на описание процесса создания схемы БД нет смысла. К тому же ничего не мешает тебе самостоятельно создать БД (ради эксперимента) и познакомиться с подходом DatabaseFirst (сначала база, потом код).
В принципе, уже на данном этапе мы можем создать контроллер с представлениями, и EF любезно сгенерирует базу данных. Однако, мы лучше сразу внесем небольшой твик в конфигурационный файл (Web.config), тем самым принудительно зададим имя базы. Найди в этом файле описание секции «ConnectionStrings» и удали из нее (если есть) все разделы. После добавь одну строку:
<add name="BugTrackerContext" connectionString="Data Source=(LocalDB)11.0;AttachDbFilename='|DataDirectory|ourBase.mdf';Integrated Security=True" providerName="System.Data.SqlClient" />
В этой строке я определил, что фреймворк должен использовать BugTrackerContext для связи с БД, а саму базу будет хранить в файле ourBase.mdf, расположенный в директории данных (App_Data) проекта.
CRUD приложение в пару кликов
Фактически наше приложение научилось работать с базой данных, но в реале мы этого еще не увидели т.к. ни разу не запускали наш баг трекер. Надо срочно исправлять положение дел, но у нас еще нет ни одного контроллера и представлений. Следовательно, запустив проект сейчас, хорошего мы ничего не увидим. Я предлагаю пока не заморачиваться на верстке представления с подготовленным нами красивым интерфейсом. Лучше быстренько протестируем то, что есть.
Создадим для этого простейший CRUD (Create read update delete) функционал без единой строчки кода. Переходи в «Solution Explorer» и добавь в папку «Controllers» новый контроллер с именем «CrudController». В окне создания нового контроллера не торопись кликать на пимпу с кнопкой «OK». В выпадающем поле «Template» выбери «MVC controller with read/write actions and views, using Entity Framework». После этого станут доступны поля для выбора классов, описывающих модель и контекст данных. Выбирай Ticket (в качестве модели) и BugTrackerContext (в качестве контекста). Остальные поля можешь оставить со значениями по умолчанию.
Нажимай на пимпу «Ok» и восхищайся, как студия создала за нас код действий с необходимыми представлениями. Нам остается только запустить приложение и проверить результат его выполнения. Сразу предупреждаю, после запуска ты увидишь не результат работы CRUD, а ошибку «Ресурс не найден». Мы не прописали маршрут по умолчанию, поэтому чтобы добраться до сгенерированного контроллера тебе потребуется вбить в адресной строке полный путь к нему: http://localhost:53532/crud. Альтернативным решением будет внесение изменений в конфигурацию маршрута (файл RouteConfig.cs):
defaults: new { controller = "crud", action = "Index", id = UrlParameter.Optional }
В этой строке я устанавливаю, что контроллером по умолчанию является crud (а не Home, как было изначально). Внеся изменения в маршрут – перезапусти приложение, и ты сразу попадешь в сгенерированный CRUD интерфейс.
Во время создания тикета ты увидишь, что фреймворк сгенерировал нам выпадающие списки для полей «Category», «Status» и «User», но в этих списках нет ни одного элемента для выбора. Чтобы в них что-то появилось нам нужно добавить данные в соответствующие таблицы. Прерви выполнение аппликации и через окно «DatabaseExplorer» открой соответствующие таблицы для внесения данных. Я создал пару статусов в таблице «Statuses» и парочку разделов в «Categories», после этого выпадающие поля в представлении заполнились добавленными данными.
Контроллеры
CRUD-приложение получилось вполне рабочим, но для постоянного использования оно не годится. Уж больно смотрится «топорно» и не функционально. У нас уже есть забутстрапенная заготовка верстки, и теперь остается ее натянуть на существующее приложение, а заодно познакомиться с самостоятельным созданием контроллеров и представлений.
Добавляй новый пустой контроллер к своему проекту и назови его «HomeController». Visual Studio сгенерирует каркас будущего контроллера и создаст действие «Index». Это действие будет выполняться во время обращение к имени контроллера. Например, чтобы обратится к нашему контроллеру «Home», нам требуется перейти по адресу: http://localhost/home.
Пока ты переписываешь код контроллера, я расскажу тебе о реализации контроллеров в ASP .NET MVC. Как ты уже понял из листинга, контроллер – это не что иное, как обычный класс, унаследованный от System.Web.Mvc.Controller. При создании нового контроллера ты должен следовать некоторым соглашениям. Главным, из которых будет обязательное наличие постфикса «Controller». Например, если ты хочешь создать контроллер «Adminka», то в этом случае полное имя должно быть «AdminkaController».
Чтобы обратится к созданному контроллеру (из браузера) необходимо написать в адресной строке полное имя контроллера и через слэш имя действия. Если требуется передать в контроллер какие-нибудь дополнительные параметры (например, методом Get), то их также следует указывать через слеш. Такой подход справедлив для маршрута, определенного по умолчанию. Например, для передачи параметра «1» контроллеру «adminka» следует пройти по пути: «http://localhost/adminka/1». Обрати внимание, что в браузере указать постфикс «Controller» не требуется.
Листинг 6. Код контроллера
private BugTrackerContext db = new BugTrackerContext();
public ActionResult Index()
{
var tickets = db.Tickets.Include(p => p.Category).Include(p => p.Status).Include(p => p.User);
return View(tickets.ToList());
}
У тебя может возникнуть резонный вопрос: «Возможно ли, как-то повлиять на установленные правила роутинга?». Шаблон маршрута определен в классе RouteConfig и ты волен им рулить как хочешь. Например: "ru{controller}/{action}/{id}
" заставит добавлять к имени контроллера префикс «ru».
Хочется еще рассказать про маршруты, но, увы, объем статьи ограничен. Пора переходить к расмсотрению небольшого примера кода, демонстрирующего обработку внешних параметров в контроллере. Думаю, комментарии излишни:
int myParam1 = Int32.Parse(Request.Params["myParam"]);
Дизайним представление
Контроллер успешно принимает пользовательские запросы, и теперь пора сотворить для него представление. Добавить для контроллера новое представление. Проще всего это сделать посредством пункта «Add View» контекстного меню, вызываемого при правом клике по имени действия контроллера.
В ASP .NET MVC принято выделять несколько типов представлений:
Ok, теперь попробуем создать наше первое представление. Поскольку в нашем приложении будет несколько повторяющихся элементов (например, общая разметка, сайдбар и т.д.). Мы можем пихать этот код в каждую вьюшку, но выгодней создать одну мастер страницу, которая впоследствии будет использоваться при формировании других представлений.
Мастер страницы принято помещать в директорию «Shared». Для своего примера я определил одно представление и назвал его _bugTrackerMasterPage.cshtml (содержит основную часть верстки). Приводить код вьюшки я не буду причине ее объема, а лучше сразу рассмотрю используемые управляющие конструкции:
Html.Partial(“_leftSidebar”)
–вызов частичного представления с именем «_leftSiderbat». В одноименном файле я описал код верстки левого сайдбара.RenderBody()
– запускает рендеринг других представлений, созданных на основе данной мастер-страницы;Url.Content("/home/createEntry")
– хелпер формирует абсолютный путь к приложению;Scripts.Render("assets/js/jquery.js")
– хелпер генерирует теги для подключения JavaScript и прописывает в атрибут src путь сценарию;Разобравшись с используемыми управляющими конструкциями, становится ясно, что весь код представлений, созданный на основе мастер-страницы будет включен на место вызова RenderBody().
Чтобы остальные представления знали на основе какой мастер-страницы им рендерится мы должны указать на нее ссылку:
@{ Layout = "~/Views/Shared/_bugTrackerMasterPage.cshtml"; }
Во время разбора кода мастер-страницы ты наверняка обратил внимание на символ «собачка», расположенный возле каждой управляющей конструкции. Все что идет после этого символа воспринимается как код на языке C#. Если требуется выполнить несколько строк управляемого кода, то для этого надо воспользоваться открывающими и закрывающими скобками.
Теперь посмотрим на код представления Index. В вьюшке формируется список имеющихся в базе тикетов. Выборка самих данных происходит в контроллере и список с ними передается в представление:
BugTrackerContext db = new BugTrackerContext();
var tickets = db.Tickets.Include(p => p.Category).Include(p => p.Status).Include(p => p.User);
return View(tickets.ToList());
Запрос к таблицам БД выполняется не напрямую, а через Entity Framework. Поскольку модель «Ticket» содержит ссылки на другие модели, то мы должны включить их в запрос (метод Include()). Сформированная выборка передается в качестве параметра соответствующему представлению.
Вывод данных из модели в самом представлении осуществляется в стандартном цикле:
@foreach (var c in Model)
Здесь я перебираю записи из Model, которую предварительно объявили в самом начале файла представления:
@model IEnumerable<BugTrackerForX.Models.Ticket>.
На этом код первого index() можно считать разобранным, самое время осуществить тестовый запуск проекта и посмотреть результат. Полный список тикетов выводится и теперь остается лишь добавить код добавления новых записей, ну это будет твоим домашним заданием.
Dispose
Разрабатывать WEB-приложения под платформу ASP.NET благодаря фреймворку ASP .NET стало значительно проще. Рассмотренный в статье пример лишнее тому подтверждение. Я не хочу сказать, что ASP .NET – это золотой костыль или серебряная пуля web-строителя. Это очередная технология, которую нужно уметь применять и получать выгоду. Мир на PHP, пусть даже с его многообразием самых разных фреймворков однозначно не заканчивается. Не стесняйся пробовать альтернативные технологии, не засиживайся в одной, пусть даже самой комфортной среде. Сравнивай технологии и выбирай из них наиболее оптимальную для конкретной задачи. На этом у меня все.
Стоит ли изучать классический ASP .NET?
Простота MVC фреймворка для ASP .NET может запросто создать ошибочное мнение. Мол, на фиг этот «неповоротливый» ASP .NET с его «странной» разметкой, мне хватит и ASP .NET MVC. Не стоит забывать, что MVC это всего лишь архитектурный паттерн и его можно реализовать самостоятельно на любом языке. Тут ситуация аналогична миру PHP. С момента появления mvc-фреймворков язык получил второе дыхание и сумел привлечь новых поклонников. Почему? Порог вхождения и в без того простой язык программирования снизился. Многие вещи стали доступны из коробки.
В мире ASP .NET произошла аналогичная ситуация. MVC фреймворк от Microsoft существенно понизил барьер вхождения в технологию ASP .NET,. Правда это не говорит, что без фреймворка платформа никуда не годится. Просто запомни, что фреймворк это лишь очередной слой, скрывающий от твоего взора ряд нюансов классического ASP .NET. Имея достаточный уровень подготовки, ты без проблем можешь сделать свою реализацию MVC фреймворк под платформу ASP .NET. Он тоже сможет в три пятнадцать делать красивые урлы и осуществлять контроль на генерируемой разметкой. К чему это я все говорю? А к тому, что технология ASP .NET намного шире, чем просто ASP .NET MVC. Ее глубокому изучения однозначно стоит уделить время и только тогда ты сможешь по-настоящему прочувствовать всю платформы от Microsoft, которая так полюбилась в корпоративном сегменте.
Топ 5 мифов об ASP .NET/ASP .NET MVC
- «А под unix-like это не заведется!». Еще как заведется. Достаточно немного почитать о проекте Mono (http://goo.gl/dS3v), а потом статью «Установка ASP .NET на Linux (nginx+mono+xsp) с Хабра (http://goo.gl/rPJbX) и миф сразу развеется в дымке;
- «Это же жутко дорого, надо много $$$». Опять неправда. Можно воспользоваться Express версии Visual Studio for Web (именно ее я использовал при написании статьи) или какой-нибудь альтернативной средой (все верно, здесь сплошная демократия). Остальные компоненты (например, SQL Server) также доступны в бесплатных вариантах. Кроме того, Microsoft регулярно проводит всяческие акции/скидки, позволяющие получить полноценный версии продуктов либо совсем бесплатно, либо с существенной скидкой.
- «Microsoft делает дырявый софт». Ну да, и это не мешает им богатеть и завоевывать новые рынки. Если серьезно, продукты Microsoft в большинстве случаев намного безопасней, чем OpenSource альтернативы. Проверить это не трудно. Заходим на баг трекер и смотрим количество ошибок под IIS (как пример) и под Apache. Результаты приятно удивят.
- «PHP приложения работают быстрей». Точно, работают. Не забывай, что код под ASP .NET будет сначала компилироваться, а только потом исполнятся. Причем именно выполнятся, а не стремиться умереть как аналогичный на PHP. Поэтому PHP если немного и побеждает, то только на фальш старте. Производительность ASP .NET хорошо показывает себя на больших проектах.
- «На PHP программировать проще». Порог вхождения действительно ниже, но не настолько как об этом твердят на каждом шагу. Время, вложенное в изучение полностью объектно-ориентированный C# окупиться на первом же более сложном проекте.
Что почитать по теме
Статья опубликована в журнале "Хакер" (http://xakep.ru). Откябрь 2013 г.
Ссылка на журнал: http://goo.gl/i4A0ZS
2014-03-07 в 04:04:23
Случайно в facebook увидел ссылку на эту статью. Зашел, думал пролистаю по диагонали. Но... дочитал до конца =)Сам тоже разрабатываю веб-приложение, но на Java с использованием Spring Framework и в частности Spring MVC.
Вот интересно сравнить технологии.Возникли такие вопросы:
1) Есть ли в ASP.NET реализация таких механизмов как Inversion of Control и Dependency Injection? Вот у тебя в коде, во всех классах, которые должны работать с базой пришлось бы создавать объектBugTrackerContext db = new BugTrackerContext();
В Spring я бы описал в контексте этот класс и по сути у меня всегда была бы его готовая реализация. И я мог бы в любой класс, где мне нужна работа БД его просто инжектить и быть уверенным, что этот объект для работы с БД уже создан и проинициализирован.
2) Есть ли стандартные механизмы для интернационализации и локализации веб-приложения?
3) И еще такой вопрос: где можно хранить всяческие настройки и параметры? Я понял, что это можно делать в файлах директории App_Start, но я про параметры, которые могут использоваться не только одним приложением.
В Java, к примеру, мы такие параметры храним в преференсах. Это стандартный механизм хранения разных настроек в Java. У нас конечно настройки подключения к БД хранятся в контекстах томкатов, или в контекстах спринга, но не мало настроек хранится и в преференсах.
P.S. Симпатичный блог!
2014-03-07 в 04:29:38
Java меня всегда манил к себе какой-то тайной, но программировать на этом языке не решался. Потом увидел .NET и в прямом смысле влюбился в C#. Этот язык вдохновил меня и теперь с удовольствием применяю его не только в десктоп приложениях, но и для WEB. К счастью, фреймворк ASP .NET MVC только способствует этому. Теперь по твоим вопросам.
1. Да, есть. В статье Компоненты без которых не обходится ни один WEB проект я как раз продемонстрировал использования DI на практике.
Для .NET существует много разных реализаций IoC, но мне больше всего нравится Ninject (примеры в статье выше) и Autofac. Это сторонние решения. Microsoft продвигает свой вариант под названием Unity. Я им не пользовался, но многие хвалят.
Все эти инжекторы похожи и отличается в первую очередь производительностью. Кстати, вот здесь http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison можно посмотреть сравнительную таблицу с разными инжекторами. Очень интересно.
2. Угу, есть. Описывать что-то конкретное тяжело, поэтому сразу привожу ссылки http://afana.me/post/aspnet-mvc-internationalization.aspx и здесь.
3. Настройки можно хранить в разных местах. Настройки приложения можно хранить в Web.config. Вообще, в большинстве случаев все вертится вокруг Configiration API.
Скоро будут статьи по разработке персонального блога на ASP .NET MVC. Несколько постов уже готовы, но я сначала хочу написать половину, а потом уже начать публикацию. Возможно, все это выльется в электронную книгу. Пока не будем загадывать :-)
Буду рад видеть в числе постоянных читателей ;-)
Kastor, попробуй оставить коммент. Нужно проверить =)
2014-03-07 в 07:25:20
Спасибо за ссылки!
После работы обязательно пройдусь по ним.
2014-03-08 в 01:49:15
Ок, скоро будет продолжение :-)