Часть 4. Модели и доступ к данным

Джон Галлоуэй

Музыкальное хранилище MVC — это учебное приложение, которое представляет и объясняет пошаговые инструкции по использованию ASP.NET MVC и Visual Studio для веб-разработки.

Музыкальный магазин MVC — это упрощенный пример реализации магазина, который продает музыкальные альбомы в Интернете и реализует базовые функции администрирования сайтов, входа пользователей и корзины для покупок.

В этой серии учебников подробно описаны все шаги, предпринятые для создания примера приложения ASP.NET MVC Music Store. В части 4 рассматриваются модели и доступ к данным.

До сих пор мы только что передавали фиктивные данные из наших контроллеров в шаблоны представлений. Теперь мы готовы подключить реальную базу данных. В этом руководстве мы рассмотрим использование SQL Server Compact Edition (часто называемого SQL CE) в качестве ядра СУБД. SQL CE — это бесплатная внедренная база данных на основе файлов, которая не требует установки или настройки, что делает ее очень удобной для локальной разработки.

Доступ к базе данных с помощью Entity Framework Code-First

Мы будем использовать поддержку Entity Framework (EF), включенную в проекты ASP.NET MVC 3, для запроса и обновления базы данных. EF — это API данных с гибким реляционным сопоставлением объектов (ORM), который позволяет разработчикам запрашивать и обновлять данные, хранящиеся в базе данных, объектно-ориентированным способом.

Entity Framework версии 4 поддерживает парадигму разработки, называемую code-first. Code-first позволяет создавать объект модели путем написания простых классов (также известных как POCO из "простых" объектов CLR) и даже может создавать базу данных на лету из ваших классов.

Изменения в классах моделей

В этом руководстве мы будем использовать функцию создания базы данных в Entity Framework. Прежде чем это сделать, внесите несколько незначительных изменений в классы моделей, чтобы добавить некоторые элементы, которые мы будем использовать позже.

Добавление классов моделей исполнителя

Наши альбомы будут связаны с художниками, поэтому мы добавим простой класс модели для описания исполнителя. Добавьте новый класс в папку Models с именем Artist.cs, используя приведенный ниже код.

namespace MvcMusicStore.Models
{
    public class Artist
    {
        public int ArtistId { get; set; }
        public string Name { get; set; }
    }
}

Обновление классов моделей

Обновите класс Album, как показано ниже.

namespace MvcMusicStore.Models
{
    public class Album
    {
        public int      AlbumId     { get; set; }
        public int      GenreId     { get; set; }
        public int      ArtistId    { get; set; }
        public string   Title       { get; set; }
        public decimal  Price       { get; set; }
        public string   AlbumArtUrl { get; set; }
        public Genre    Genre       { get; set; }
        public Artist   Artist      { get; set; }
    }
}

Затем внесите следующие обновления в класс Genre.

using System.Collections.Generic;
 
namespace MvcMusicStore.Models
{
    public partial class Genre
    {
        public int      GenreId     { get; set; }
        public string   Name        { get; set; }
        public string   Description { get; set; }
        public List<Album> Albums   { get; set; }
    }
}

Добавление папки App_Data

Мы добавим каталог App_Data в проект для хранения файлов базы данных SQL Server Express. App_Data — это специальный каталог в ASP.NET который уже имеет правильные разрешения на доступ к базе данных. В меню Проект выберите Добавить папку ASP.NET, а затем App_Data.

Снимок экрана: меню

Создание строки подключения в файле web.config

Мы добавим несколько строк в файл конфигурации веб-сайта, чтобы Платформа Entity Framework знала, как подключиться к базе данных. Дважды щелкните файл Web.config, расположенный в корне проекта.

Снимок экрана: файл веб-конфигурации в обозревателе решений для создания строки подключения в нем.

Прокрутите вниз этого файла и добавьте <раздел connectionStrings> непосредственно над последней строкой, как показано ниже.

<connectionStrings>
    <add name="MusicStoreEntities"
     connectionString="Data Source=|DataDirectory|MvcMusicStore.sdf"
     providerName="System.Data.SqlServerCe.4.0"/>
  </connectionStrings>  
</configuration>

Добавление класса контекста

Щелкните правой кнопкой мыши папку Models и добавьте новый класс с именем MusicStoreEntities.cs.

Снимок экрана: папка Models для добавления класса контекста.

Этот класс будет представлять контекст базы данных Entity Framework и будет обрабатывать наши операции создания, чтения, обновления и удаления. Ниже приведен код для этого класса.

using System.Data.Entity;
 
namespace MvcMusicStore.Models
{
    public class MusicStoreEntities : DbContext
    {
        public DbSet<Album> Albums { get; set; }
        public DbSet<Genre> Genres { get; set; }
    }
}

Вот и все - нет другой конфигурации, специальных интерфейсов и т. д. Расширяя базовый класс DbContext, класс MusicStoreEntities может обрабатывать операции с базой данных. Теперь, когда мы подключились к этому, давайте добавим еще несколько свойств в классы моделей, чтобы воспользоваться некоторыми дополнительными сведениями в базе данных.

Добавление данных каталога магазина

Мы воспользуемся функцией в Entity Framework, которая добавляет "начальные" данные в только что созданную базу данных. Это позволит предварительно заполнить каталог магазина списком жанров, исполнителей и альбомов. Скачиваемый MvcMusicStore-Assets.zip, который включал файлы макета сайта, которые использовались ранее в этом руководстве, содержит файл класса с начальными данными, расположенный в папке с именем Code.

В папке Code/Models найдите файл SampleData.cs и добавьте его в папку Models в нашем проекте, как показано ниже.

Снимок экрана: папка Code or Models для поиска файла Sample Data C S и добавления данных каталога хранилища.

Теперь нам нужно добавить одну строку кода, чтобы сообщить Entity Framework об этом классе SampleData. Дважды щелкните файл Global.asax в корне проекта, чтобы открыть его, и добавьте следующую строку в верхнюю часть метода Application_Start.

protected void Application_Start()
{
    System.Data.Entity.Database.SetInitializer(
    new MvcMusicStore.Models.SampleData());
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
 }

На этом этапе мы завершили работу, необходимую для настройки Entity Framework для нашего проекта.

Запрос к базе данных

Теперь давайте обновим storeController, чтобы вместо использования "фиктивных данных" он вызывал в нашу базу данных для запроса всей информации. Начнем с объявления поля в StoreController для хранения экземпляра класса MusicStoreEntities с именем storeDB:

public class StoreController : Controller
{
    MusicStoreEntities storeDB = new MusicStoreEntities();

Обновление индекса хранилища для запроса базы данных

Класс MusicStoreEntities поддерживается Entity Framework и предоставляет свойство коллекции для каждой таблицы в базе данных. Давайте обновим действие Индекса StoreController, чтобы получить все жанры в базе данных. Ранее мы делали это с помощью жесткой кодировки строковых данных. Теперь мы можем использовать коллекцию Generes контекста Entity Framework:

public ActionResult Index()
{
    var genres = storeDB.Genres.ToList();
    return View(genres);
 }

Никаких изменений в шаблоне Представления не должно быть, так как мы по-прежнему возвращаем тот же StoreIndexViewModel, который мы возвращали ранее. Сейчас мы просто возвращаем динамические данные из базы данных.

Когда мы снова запустим проект и зайдем по URL-адресу /Store, мы увидим список всех жанров в базе данных:

Снимок экрана: список всех жанров в базе данных.

Обновление обзора и сведений о Магазине для использования динамических данных

С помощью метода действия /Store/Browse?genre=[some-genre] мы ищем жанр по имени. Мы ожидаем только один результат, так как у нас не должно быть двух записей для одного и того же имени жанра, поэтому мы можем использовать . Расширение Single() в LINQ для запроса соответствующего объекта Genre, как показано ниже (пока не вводите этот текст):

var example = storeDB.Genres.Single(g => g.Name == "Disco");

Метод Single принимает лямбда-выражение в качестве параметра, который указывает, что нам нужен один объект Genre таким образом, чтобы его имя соответствовало значению, которое мы определили. В приведенном выше случае мы загружаем один объект Genre со значением Name, соответствующим Disco.

Мы воспользуемся функцией Entity Framework, которая позволяет указывать другие связанные сущности, которые мы хотим загрузить, а также при извлечении объекта Genre. Эта функция называется формированием результатов запросов и позволяет сократить количество раз, необходимых для доступа к базе данных, чтобы получить всю необходимую информацию. Мы хотим предварительно получить альбомы для жанра, поэтому мы обновим наш запрос, чтобы включить из Genres.Include("Альбомы"), чтобы указать, что нам также нужны связанные альбомы. Это более эффективно, так как данные о жанре и альбоме будут извлекаться в одном запросе базы данных.

С объяснениями из пути, вот как выглядит наше обновленное действие обзор контроллера:

public ActionResult Browse(string genre)
{
    // Retrieve Genre and its Associated Albums from database
    var genreModel = storeDB.Genres.Include("Albums")
        .Single(g => g.Name == genre);

    return View(genreModel);
}

Теперь можно обновить представление обзора Магазина, чтобы отобразить альбомы, доступные в каждом жанре. Откройте шаблон представления (находится в /Views/Store/Browse.cshtml) и добавьте маркированный список альбомов, как показано ниже.

@model MvcMusicStore.Models.Genre
@{
    ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
    @foreach (var album in Model.Albums)
    {
        <li>
            @album.Title
        </li>
    }
</ul>

Запуск приложения и переход в папку /Store/Browse?genre=Jazz показывает, что наши результаты теперь извлекаются из базы данных, отображая все альбомы в выбранном жанре.

Снимок экрана: результаты, полученные из базы данных, отображают все альбомы в выбранном жанре.

Мы изменим URL-адрес /Store/Details/[id] и заменим фиктивные данные запросом к базе данных, который загружает альбом, идентификатор которого соответствует значению параметра.

public ActionResult Details(int id)
{
    var album = storeDB.Albums.Find(id);
 
    return View(album);
}

Запуск приложения и переход в /Store/Details/1 показывает, что наши результаты теперь извлекаются из базы данных.

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

Теперь, когда на странице Сведений о магазине настроено отображение альбома по идентификатору альбома, давайте обновим представление Обзор , чтобы создать ссылку на представление Сведения. Мы будем использовать Html.ActionLink точно так же, как и для связывания из индекса Магазина в Магазин обзор в конце предыдущего раздела. Ниже отображается полный источник для представления Обзор.

@model MvcMusicStore.Models.Genre
@{
    ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
    @foreach (var album in Model.Albums)
    {
        <li>
            @Html.ActionLink(album.Title,
"Details", new { id = album.AlbumId })
        </li>
    }
</ul>

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

Снимок экрана: возможность перехода со страницы Магазина на страницу жанра.