В этой статье я бы хотел рассмотреть возможность быстрого создания функционального прототипа веб приложения на основе технологии ASP.NET MVC 3. Немного соприкоснувшись с «Ruby on Rails», я заинтересовался его возможностью Scaffolding-га т.е. быстрого способа генерации основного каркаса приложения. Это дает возможность разработчику сконцентрироваться на создании бизнес модели и получить функциональный прототип не тратя свое время на детали реализации, что очень важно в начале любого проекта. А существует что-либо подобное в ASP.NET MVC? И насколько это функционально, быстро, удобно и практично.
Я хотел бы рассмотреть это на конкретном примере, и в этом качестве предлагаю создать простой веб каталог товаров. В моем случае это будет каталог автозапчастей.
В качестве инструментов, я буду использовать следующие средства разработки и библиотеки.
Прежде всего, необходимо настроить наше окружение. Найдите в менеджере расширений Visual Studio расширение NuGet и установите его. Либо скачайте и установите с официального сайта.
Создайте новый ASP.NET MVC 3 проект.
Далее нужно с помощью командной стоки менеджера пакетов установить MvcScaffolding. Откройте консоль NuGet: Главное меню «View” → „Other Windows“ → „Package Manager Console“.
Как мы увидим позже, эта командная строка является основным средством работы с MvcScaffolding, и аналогична командной стоке в Rails.
Для установки пакета достаточно выполнить команду:
Как можно видеть, эта команда установит пакет MvcScaffolding в ваш проект, а также пакет EFCodeFirst, который находится в зависимости(в моем случае я установил EFCodeFirst раньше).
Приложение, которое я собираюсь разработать в этом примере, является простым каталогом автозапчастей и может являться частью магазина или учетной системы.
Для начала, нам нужно смоделировать предметную область.
Итак, что у нас есть:
Создадим основные классы бизнес сущностей.
Как можно видеть, у нас есть отношение „один-ко-многим“ между „Моделью“, „Маркой“ и „Выпуском“ а так же отношение „многие-ко-многим“ между „Запчастями“ и „Выпуском“. Мне было интересно как MvcScaffolding отработает эти отношение и какой код будет создан в результате. Забегая вперед, нужно отметить следующие правила, которые нужно соблюдать при описании классов бизнес модели:
В нашем случае в „Model“ мы включили поле MakeId – внешний ключ от „Mark“, а так же виртуальное поле Make. Это реализует отношение „один-ко-многим“. Для реализации отношения „многие-ко-многим“ мы добавили соответствующие свойства-списки в классы „Part“ и „Release“
Для парадигмы CodeFirst этого описания классов достаточно: нам нет нужды заботится о хранении и генерации, все будет делаться автоматически, при старте приложения. После окончания работы с моделью необходимо обязательно собрать проект.
Для работы с MvcScaffolding необходимо открыть командную стоку менеджера пакетов, о которой я рассказывал выше.
Этот шаг не обязательный но давайте, для начала, кастомизируем наши шаблоны, и выполним следующие команды:
В результате в нашем проекте появиться новая папки с шаблонами.
Теперь можно редактировать эти шаблоны для того, чтобы генерируемый код соответствовал нашим представлениям и разметке.
Далее создадим наш первый контроллер:
Тут:
В результате у нас создаются следующие файлы и классы
Я не буду тут приводить генерируемый код, тем кому интересно могут скачать исходники проекта по ссылке в конце статьи.
Теперь давайте аналогично создадим второй контроллер:
Откроем созданное представление _CreateOrEdit.cshtml и посмотрим на сгенерированный код:
А так же в коде контроллера:
С ситуацией отношения „один-ко-многим“ MvcScaffolding справился очень хорошо: в представлении редактирования „Model“ был создан выпадающий список, который заполняется значениями из „Mark“.
Продолжим создавать наши контроллеры:
Соответствующие контроллеры, представления и репозитории были созданы. Тут также с отношением „один-ко-многим“ практически все хорошо(можно лишь заменить свойство Name на FullName в параметрах создания выпадающего списка для лучшего отображения), однако, для отношения „многие-ко-многим“ MvcScaffolding ничего не создал, так, что нам нужно либо добавлять это вручную или изменять шаблон генерации (эта отдельная тема, я постараюсь написать и дать пример реализации в другой статье и сообщить автору MvcScaffolding, возможно он включит это в следующий релиз).
Теперь нам осталось только создать репозитории для справочников и других классов, у которых нет контроллеров.
Настало время первого запуска.
Перед этим, необходимо записать в конфигурацию строку подключения к базе данных. Если это SQL Express, то допускается отсутствие физического файла.
Важно, что бы имя строки подключения совпадало с именем класса контекста.
Кроме этого, я бы хотел иметь тестовые данные и заполненные справочники. Для этого добавим класс, унаследованный от класса DropCreateDatabaseAlways, который будет каждый раз инициализировать нашу базу данных.
А в Global.asax при старте приложения добавим:
Подправив немного полученный код, мы получили функционально рабочее приложение.
В результате на создание функционального прототипа я потратил всего пару часов, со всеми доработками.
Возможности, которых мне явно не хватало при работе:
Хотя все это не критично и можно реализовать за счет кастомизации и метапрограммирования в шаблонах.
Пока что, EFCodeFirst и MvcScaffolding находятся в стадии бета, но они вполне работоспособны и являются достаточно мощными средствами для быстрого старта и разработки веб приложений.
Будем наедятся, в скором будущем, данные средства будут иметь более расширенный функционал и станут частью официального релиза ASP.NET MVC.
Блог автора MvcScaffolding
Исходный код примера
Я хотел бы рассмотреть это на конкретном примере, и в этом качестве предлагаю создать простой веб каталог товаров. В моем случае это будет каталог автозапчастей.
В качестве инструментов, я буду использовать следующие средства разработки и библиотеки.
- MS Visual Studio 2010
- ASP.NET MVC 3 – официальный релиз
- NuGet – менеджер пакетов для Visual Studio
- MvcScaffolding — генератор кода MVC на основе шаблонов
- EFCodeFirst — новая возможность Entity Framework CPT5
Установка и настройка
Прежде всего, необходимо настроить наше окружение. Найдите в менеджере расширений Visual Studio расширение NuGet и установите его. Либо скачайте и установите с официального сайта.
Создайте новый ASP.NET MVC 3 проект.
Далее нужно с помощью командной стоки менеджера пакетов установить MvcScaffolding. Откройте консоль NuGet: Главное меню «View” → „Other Windows“ → „Package Manager Console“.
Как мы увидим позже, эта командная строка является основным средством работы с MvcScaffolding, и аналогична командной стоке в Rails.
Для установки пакета достаточно выполнить команду:
Install-Package MvcScaffolding
Как можно видеть, эта команда установит пакет MvcScaffolding в ваш проект, а также пакет EFCodeFirst, который находится в зависимости(в моем случае я установил EFCodeFirst раньше).
Модель данных и предметная область приложения
Приложение, которое я собираюсь разработать в этом примере, является простым каталогом автозапчастей и может являться частью магазина или учетной системы.
Для начала, нам нужно смоделировать предметную область.
Итак, что у нас есть:
- Марка машины
- Модель
- Год выпуска
- Запасная часть
Создадим основные классы бизнес сущностей.
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public List<Model> Models { get; set; }
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public int MakeId { get; set; }
[NotMapped]
public string FullName
{
get { return string.Format("{0} {1}", Make.Name, Name); }
}
virtual public Make Make { get; set; }
public List<Release> Years { get; set; }
}
public class Release
{
public int Id { get; set; }
public int ModelId { get; set; }
public int YearId { get; set; }
[NotMapped]
public string FullName
{
get { return string.Format("{0} ({1})", Model.FullName, Year.Value); }
}
virtual public Model Model { get; set; }
virtual public Year Year { get; set; }
public List<Part> Parts { get; set; }
}
public class Part
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public int GroupId { get; set; }
virtual public Group Group { get; set; }
public List<Release> Releases { get; set; }
}
* This source code was highlighted with Source Code Highlighter.
Как можно видеть, у нас есть отношение „один-ко-многим“ между „Моделью“, „Маркой“ и „Выпуском“ а так же отношение „многие-ко-многим“ между „Запчастями“ и „Выпуском“. Мне было интересно как MvcScaffolding отработает эти отношение и какой код будет создан в результате. Забегая вперед, нужно отметить следующие правила, которые нужно соблюдать при описании классов бизнес модели:
- Указывать первичный ключ. Это может быть Id, {ИмяСущьности}Id либо атрибут [Key]
- Указать внешний ключ {ИмяСущьности}Id
- Нет поддержки комплексных первичных ключей. Но может это и к лучшему, потому как с ними много других проблем.
В нашем случае в „Model“ мы включили поле MakeId – внешний ключ от „Mark“, а так же виртуальное поле Make. Это реализует отношение „один-ко-многим“. Для реализации отношения „многие-ко-многим“ мы добавили соответствующие свойства-списки в классы „Part“ и „Release“
Для парадигмы CodeFirst этого описания классов достаточно: нам нет нужды заботится о хранении и генерации, все будет делаться автоматически, при старте приложения. После окончания работы с моделью необходимо обязательно собрать проект.
Настройка шаблона и создание контроллеров
Для работы с MvcScaffolding необходимо открыть командную стоку менеджера пакетов, о которой я рассказывал выше.
Этот шаг не обязательный но давайте, для начала, кастомизируем наши шаблоны, и выполним следующие команды:
Scaffold CustomTemplate MvcScaffolding.RazorView _CreateOrEdit
Scaffold CustomTemplate MvcScaffolding.RazorView Edit
Scaffold CustomTemplate MvcScaffolding.RazorView Index
Scaffold CustomTemplate MvcScaffolding.Controller ControllerWithRepository
В результате в нашем проекте появиться новая папки с шаблонами.
Теперь можно редактировать эти шаблоны для того, чтобы генерируемый код соответствовал нашим представлениям и разметке.
Далее создадим наш первый контроллер:
Scaffold Controller Make -Verbose -ControllerName Make -DbContextType ApartsContext -Repository
Тут:
- Scaffold Controller — команда создания контроллера
- Make – имя класс модели
- -ControllerName Make— указываем имя контроллера, не обязательно, но по умолчанию генериться множественная форма от имени модели
- -DbContextType ApartsContext — класс контекста
- -Repository — указывает, что необходимо использовать доступ к данным через репозиторий
В результате у нас создаются следующие файлы и классы
- Контроллер MakeController, который содержит методы создания, удаления, изменения и отображения в виде списка для сущьности Make
- Контекст ApartsContext (если он не был создан), который обеспечивает связь с хранилищем
- Репозиторий MakeRepository — реализация основных базнес правил
- Файлы представлений _CreateOrEdit.cshtml, Create.cshtml Delete.cshtml Details.cshtml Edit.cshtml Index.cshtml
Я не буду тут приводить генерируемый код, тем кому интересно могут скачать исходники проекта по ссылке в конце статьи.
Теперь давайте аналогично создадим второй контроллер:
Scaffold Controller Model -Verbose -ControllerName Model -DbContextType ApartsContext -Repository
Откроем созданное представление _CreateOrEdit.cshtml и посмотрим на сгенерированный код:
...
@Html.DropDownListFor(model => model.MakeId, ((IEnumerable<ApartCatalogMvc.Models.Make>)ViewBag.PossibleMakes).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.Id.ToString(),
Selected = (Model != null) && (option.Id == Model.MakeId)
}), "Choose...")
...
* This source code was highlighted with Source Code Highlighter.
А так же в коде контроллера:
...
public ActionResult Edit(int id)
{
ViewBag.PossibleMakes = makeRepository.GetAllMakes();
...
* This source code was highlighted with Source Code Highlighter.
С ситуацией отношения „один-ко-многим“ MvcScaffolding справился очень хорошо: в представлении редактирования „Model“ был создан выпадающий список, который заполняется значениями из „Mark“.
Продолжим создавать наши контроллеры:
Scaffold Controller Release -Verbose -ControllerName Releases -DbContextType ApartsContext -Repository
Scaffold Controller Part -Verbose -ControllerName Part -DbContextType ApartsContext -Repository
Соответствующие контроллеры, представления и репозитории были созданы. Тут также с отношением „один-ко-многим“ практически все хорошо(можно лишь заменить свойство Name на FullName в параметрах создания выпадающего списка для лучшего отображения), однако, для отношения „многие-ко-многим“ MvcScaffolding ничего не создал, так, что нам нужно либо добавлять это вручную или изменять шаблон генерации (эта отдельная тема, я постараюсь написать и дать пример реализации в другой статье и сообщить автору MvcScaffolding, возможно он включит это в следующий релиз).
Теперь нам осталось только создать репозитории для справочников и других классов, у которых нет контроллеров.
Scaffold Repository Year -DbContextType ApartsContext -Verbose
Scaffold Repository Group -DbContextType ApartsContext -Verbose
Настало время первого запуска.
Перед этим, необходимо записать в конфигурацию строку подключения к базе данных. Если это SQL Express, то допускается отсутствие физического файла.
<add name="ApartsContext"
connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\ApartsDB.mdf;Integrated Security=True;User Instance=True;Initial Catalog=ApartsDB"
providerName="System.Data.SqlClient"/>
* This source code was highlighted with Source Code Highlighter.
Важно, что бы имя строки подключения совпадало с именем класса контекста.
Кроме этого, я бы хотел иметь тестовые данные и заполненные справочники. Для этого добавим класс, унаследованный от класса DropCreateDatabaseAlways, который будет каждый раз инициализировать нашу базу данных.
public class ApartsInitializer : DropCreateDatabaseAlways<ApartsContext>
{
protected override void Seed(ApartsContext context)
{
var makes = new List<Make>
{
new Make { Name = "BMW"},
new Make { Name = "Mazda" }
};
makes.ForEach(m => context.Makes.Add(m));
...
* This source code was highlighted with Source Code Highlighter.
А в Global.asax при старте приложения добавим:
...
DbDatabase.SetInitializer<ApartsContext>(new ApartsInitializer());
...
* This source code was highlighted with Source Code Highlighter.
Подправив немного полученный код, мы получили функционально рабочее приложение.
Заключение
В результате на создание функционального прототипа я потратил всего пару часов, со всеми доработками.
Возможности, которых мне явно не хватало при работе:
- Пейджинг и фильтрация в списках
- Поддержка отношений „многие-ко-многим“
- Биндинг для различных типов данных, например для DataTme
Хотя все это не критично и можно реализовать за счет кастомизации и метапрограммирования в шаблонах.
Пока что, EFCodeFirst и MvcScaffolding находятся в стадии бета, но они вполне работоспособны и являются достаточно мощными средствами для быстрого старта и разработки веб приложений.
Будем наедятся, в скором будущем, данные средства будут иметь более расширенный функционал и станут частью официального релиза ASP.NET MVC.
Дополнительные материалы
Блог автора MvcScaffolding
Исходный код примера