Основы. Часть 7 – Создание частей веб-приложения из заготовок

Итак, все готово к созданию исходного кода на основе заготовок. Установлены требуемые инструменты, отредактированы необходимые файлы. Давайте сгенерируем Контроллеры и Представления.

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

Генерируем исходный код

Для создания исходного кода достаточно воспользоваться командой Scaffold:

PM> Scaffold Controller Tag -DbContextType CatalogContext -Repository -ReferenceScriptLibraries
.........
PM> Scaffold Controller Publisher -DbContextType CatalogContext -Repository -ReferenceScriptLibraries
.........
PM> Scaffold Controller Language -DbContextType CatalogContext -Repository -ReferenceScriptLibraries
.........
PM> Scaffold Controller BookDetails -ControllerName Catalog -DbContextType CatalogContext -Repository -ReferenceScriptLibraries
.........

В результате для каждого класса Модели будут созданы:

  • Контроллер, имя которого получено исходя из названия указанного класса Модели. Например, для класса Tag будет сгенерирован TagsController, реализующий Контроллер Tags. В последний будут добавлены следующие Действия:
    • Index – для вывода таблицы с записями из базы данных;
    • Create – с его помощью можно ввести и сохранить новую запись;
    • Edit – предназначено для редактирования информации;
    • Delete – служит для удаления выбранной записи (с подтверждением).
  • Класс, реализующий репозиторий для взаимодействия с базой данных. Он будет назван исходя из следующего шаблона: <имя класса Модели>Repository. В том же файле расположится описание его интерфейса.
  • Для Представлений, необходимых для указанных выше Действий, в папке View будет создана подпапка с именем, аналогичным имени Контроллера.

Обратите внимание, что для класса BookDetails определено свое имя Контроллера – Catalog. Соответственно, его реализация будет размещена в классе CatalogController.

Копируем исходные файлы

При модификации заготовок было решено изменить их пространства имен и, соответственно, место расположение некоторых файлов с исходным кодом. Поэтому в папке Models создадим подпапки:

  1. Repositories, куда перенесем все файлы с реализацией репозиториев.
  2. DbContext, где будет расположен контекст базы данных. Кроме того, изменим его пространство имен с BookCatalog.Models на BookCatalog.Models.DbContext.
namespace BookCatalog.Models.DbContext
{
    using System.Data.Entity;

    public class CatalogContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, add the following
        // code to the Application_Start method in your Global.asax file.
        // Note: this will destroy and re-create your database with every model change.
        // 
        // System.Data.Entity.Database.SetInitializer(new System.Data.Entity.DropCreateDatabaseIfModelChanges<BookCatalog.Models.CatalogContext>());

        public DbSet<BookCatalog.Models.Tag> Tags { get; set; }

        public DbSet<BookCatalog.Models.Publisher> Publishers { get; set; }

        public DbSet<BookCatalog.Models.Language> Languages { get; set; }

        public DbSet<BookCatalog.Models.BookDetails> BookDetails { get; set; }
    }
}

Также можно удалить файлы Details.cshtml в папках представлений, так как соответствующее Действие было удалено из Контроллеров еще в заготовках.

Теперь остается сделать несколько настроек чтобы получить работающее веб-приложение. Обратите внимание, что если бы заготовки уже были готовые, то все свелось бы к 4 шагам:

  1. Создание проекта;
  2. Установка инструмента для работы с заготовками (1 команда в консоле NuGet);
  3. 4 команды Scaffold для создания исходного кода;
  4. Небольшие изменения в настройках, которые будут рассмотрены ниже.

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

Последние настройки

Контроллер по умолчанию

Давайте заглянем в файл Global.asax и посмотрим на метод RegisterRoutes(). В нем указано, что Контроллером по умолчанию является Home. В разрабатываемом веб-приложении в этой роли будет выступать Catalog. Поэтому просто заменим имя в параметре метода MapRoute():

namespace BookCatalog
{
    using System.Web.Mvc;
    using System.Web.Routing;

    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new {
                    controller = "Catalog",
                    action = "Index",
                    id = UrlParameter.Optional
                } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
    }
}

CSS для таблиц

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

Корректировки в классе контекста

Чуть-чуть доработаем класс CatalogContext. В частности:

  • добавим директиву using для пространства имен BookCatalog.Models и уберем его упоминания в свойствах.
  • С помощью рефакторинга изменим имя свойства BookDetails на более логично Books.

Вывод списка ключевых слов

В Представлении Catalog/Index для каждой книги должен отображаться список присвоенных её ключевых слов. Сейчас этот код выглядит следующим образом:

<td>@item.Tags</td>

Однако необходимо учесть, что item.Tags это коллекция, предоставляющая интерфейс ICollection<T>. В данном случае будет вызван её метод ToString(), который просто вернет имя типа. Это не имеет смысла. Поэтому необходимо организовать вывод списка. А поскольку Razor поддерживает использование C#  кода, то это сделать можно следующим образом:

@model IEnumerable<BookCatalog.Models.BookDetails>
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>@Html.ActionLink("Create New", "Create")</p>

<table>
    <tr>
        <th></th>
        <th>Title</th>
        <th>Author</th>
        <th>PublishedAt</th>
        <th>Url</th>
        <th>Description</th>
        <th>Tags</th>
        <th>Rating</th>
        <th>IsFree</th>
        <th>IsVisible</th>
        <th>Language</th>
        <th>Publisher</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
        <td>@item.Title</td>
        <td>@item.Author</td>
        <td>@String.Format("{0:D}", item.PublishedAt)</td>
        <td>@item.Url</td>
        <td>@item.Description</td>
        <td>@string.Join(", ", item.Tags.Select(tag => tag.Text).ToArray())</td>
        <td>@item.Rating</td>
        <td>@item.IsFree</td>
        <td>@item.IsVisible</td>
        <td>@(item.Language == null ? "None" : item.Language.Name)</td>
        <td>@(item.Publisher == null ? "None" : item.Publisher.Title)</td>
    </tr>
}

</table>

В результате вызова Join() будет создана строка из ключевых слов, разделенных запятой с пробелом.

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

Меню

Для навигации по создаваемому веб-приложению можно самостоятельно вводить ссылки для перехода к нужному Действию определенного Контроллера. Для этого достаточно соблюдать следующий формат:
http://<hostname>/<ControllerName>/<Action>/

Однако использовать меню гораздо удобнее. И для того, чтобы отобразить его на всех страницах, добавим следующий код в шаблон раскладки (файл _Layout.cshtml):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>

<body>
    <div class="menu">
        @Html.ActionLink("Catalog", "Index", "Catalog")&nbsp;|&nbsp;
        @Html.ActionLink("Publishers", "Index", "Publishers")&nbsp;|&nbsp;
        @Html.ActionLink("Languages", "Index", "Languages")&nbsp;|&nbsp;
        @Html.ActionLink("Tags", "Index", "Tags")
    </div>
    @RenderBody()
</body>
</html>

Метод Html.ActionLink() используются для создания ссылки на указанное Действие (в данном случае всегда Index) выбранного Контроллера.

Настраиваем соединение с базой данных

На начальном этапе в качестве СУБД удобнее всего воспользоваться SQL Server Compact. Поэтому необходимо в корневой папке проекта создать специальную ASP.NET папку под названием App_Data. В ней и будет размещен файл с базой данных. Для этого в Solution Explorer / Solution Navigator в контекстом меню нужно выбрать следующий пункт: Add > Add ASP.NET Folder > App_Data.

Теперь необходимо указать веб-приложению, какую СУБД будем использовать. Для этого в файле Web.config добавим строку соединения (connectionStrings), которая выглядит следующим образом:

<?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=152368
  -->

<configuration>
  <appSettings>
    <add key="webpages:Version" value="1.0.0.0"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
  </appSettings>

  <connectionStrings>
    <add name="CatalogContext"
         connectionString="Data source=|DataDirectory|\BooksCatalog.sdf"
         providerName="System.Data.SqlServerCe.4.0" />
  </connectionStrings>
    
  <system.web>

Обратите на следующие моменты:

  • секция со строками соединения располагаются внутри секции <configuration>;
  • имя строки соединения совпадает с именем класса контекста базы данных CatalogContext;
  • connectionString указывает где будет размещен файл с базой данных и его имя;

Запускаем веб-предложение

Все готово. Запустим созданное веб-приложение. В начале будет отображена пустая таблица каталога. Используя меню пройдемся по разделам и посмотрим что в них содержится.

Раздел Tags

Здесь отображается список ключевых слов. Добавим несколько, например: ASP.NET, MVC, C#. Обратите внимание, что есть возможность редактирования и удаления. Кроме того, отображается число книг, которые отмечены тем или иным ключевым словом.

Раздел Languages

В этом разделе укажем пару названий языков: English и Русский.

Раздел Publishers

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

Раздел Catalog

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

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

Давайте введем данные любой книги, но с обязательным указанием издателя. Теперь, если вернуться в раздел Publishers, то можно обнаружить, что число книг теперь стало равно единице. Кроме того, если удалить из списка языков тот, что был выбран для книги, то и данные о книге книга будут также удалены.

Закроем веб-приложение. Здесь хочется еще раз отметить скорость разработки. При наличии заготовок текущая версия веб-приложения создается за считанные минуты.

Если сейчас запустить проект повторно, то можно отметить, что все данные остались как на момент его завершения. Чуть выше была сделана настройка для использоваться SQL Server Compact в качестве СУБД. Но откуда взялась сама база данных? Выясним это чуть позже. А пока посмотрим что необходимо изменить, чтобы получ��ть проект, который будет хорошей основой для изучения ASP.NET MVC 3.


Исходный код проекта (C#, Visual Studio 2010): mvc3-in-depth-basics-07.zip

Комментарии (24) -

Возможно, правильно писать так:


@Html.ActionLink("Publishers", "Index", "Publisher")&nbsp;|&nbsp;
@Html.ActionLink("Languages", "Index", "Language")&nbsp;|&nbsp;
@Html.ActionLink("Tags", "Index", "Tag")

А то не работало.

Последний параметр - имя Контроллера (по сути - имя соответствующего класса без суффикса Controller). Проверьте имена. Возможно у вас это LanguageController (тогда как в демонстрационном проекте это LanguagesController). Тогда и ссылки будут другие.

Добрый день. Затык на команде

Scaffold Controller Tag -DbContextType CatalogContext -Repository -ReferenceScriptLibraries
Ругается: BookCatalog.Models.DbContext.CatalogContext is not a System.Data.Entity.
DbContext class and does not contain a 'Tags' property, so it cannot be used as the database context.

Исходник класса полностью повторяет заготовку, а на вашем листинге, есть свойства для классов Tag, Publisher и т.д. Их нужно писать руками/вставлять в заготовку? Не могу понять что я упустил Frown  

@ gamu: А до выполнения команды CatalogContext существовал? Он должен создаваться при ей выполнении. В принципе он может быть и до этого момента, но тогда его объявление должно выглядеть примерно так:

public class CatalogContext : DbContext { ... }

Андрей 27.12.2011 1:22:45

Andrey, У меня вопросы: 1. У в результате создания приложения по заготовкам у меня получились файлы CatalogContext.cs и BookCatalogContext.cs!
Они оба должны быть или второй лишний????
2. Если оба нужны тогда почему второй файл дает ошибку типа 'BookCatalog.Models.DbContext' is a 'namespace' but is used like a 'type'  

Андрей 27.12.2011 1:50:01

Скачал твои исходники, в них файла BookCatalogContext.cs нет. Видимо он не нужен, только вот откуда он у меня взялся????

@ Андрей: Должен быть только CatalogContext. Видимо где-то вы пропустили указание имени класса крнтекста и был создан новый контекст с именем по умолчанию.

Добрый день
В готовом примере из mvc3-in-depth-basics-07.zip невозможно редактировать Tags. Есть label, но нет самого поля.А так все великолепно работает.Здорово!

@ Alex: А можно точнее как у вас это вышло (что нет поля)? Сейчас еще раз запустил пример - все на месте. Если добавить метку, то по адресу http://localhost:[port]/Tags/Edit/1 выводит форму редактирования с полем.

Ошибка сервера в приложении '/'.
Не удалось найти данный ресурс.
Описание: HTTP 404. Возможно, искомый ресурс (или один из зависимых от него компонентов) удален, получил другое имя или временно недоступен.  Просмотрите следующий URL-адрес и проверьте, что он введен правильно.

Запрошенный URL: /Publishers

ВОТ ЭТО появляется при нажатии на кнопки в меню. Этот пример касается ссылки на Публикации.

@ Andriy: Ищите ошибку. Пример приведенный в конце статьи работоспособный. Может вы назвали Контроллер Publisher (без 's')?

Alex :
В готовом примере из mvc3-in-depth-basics-07.zip невозможно редактировать Tags. Есть label, но нет самого поля.
Совершенно верно. В проекте кот. делается по описанным шагам в соотвествующем файле вообще нет упоминания о Tags. В Вашем примере он прописан. Но результата нет.
PS. С предыдущими проблемами разобрался. СПС ты был прав насчет -s. ))))

А и еще поподробнее поясню проблему. Редактирование для Tags есть. Нет (а точнее есть только label) Tags в добавлении новых и редактировании старых книг.

@ Andriy: Я же указал причину. Tags редактировать планировалось позже, т.к. просто вводом строки тут не обойтись.

Да, если делать всё по шагам получаются контроллеры LanguageController.cs и т.д. без s. Нужно всем контроллерам кроме CatalogController в название s добавить - и всё отлично. пример: TagsController.cs

@ Michael: А какая у вас VS (версия, редакция и язык)?

После просмотра ваших примеров кода, в VS перестают подсвечиваться подсказки в вашем и во всех существующих проектах. Т. е. не просто resharper, а полностью. Не сталкивались с таким? Как решить эту проблему?

И ещё в вашем коде возникает такая ошибка: "It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS."
Не подскажете как с ней бороться?

@ Pasha: Про подсказки не сталкивался, а с ошибкой - очистите весь solution и build заново.

По ссылке исходного кода скачивается файл file.axd. Это что?

@ zh008:
Проверил сейчас. Скачивается zip архив. Попробуйте еще раз.

Дмитрий 02.08.2013 14:22:29

В "Генерируем исходный код" измените имена контроллеров, чтобы они были с буквой `s` (Tag -> Tags, ...)

Дмитрий 02.08.2013 14:34:35

И ещё одно, у меня в "/Catalog/Create" не отображается поле Tag

@ Дмитрий: Там и так должны создаваться с "s".

Про поле Tags в тексте отмечено, что готового элемента управления для него нет. А значит нет и самого поля.

Добавить комментарий