パート 4: モデルとデータ アクセス

作成者: Jon Galloway

MVC Music Store は、ASP.NET MVC と Visual Studio を使用した Web 開発の手順を段階的に紹介し、説明するチュートリアル アプリケーションです。

MVC Music Store は、音楽アルバムをオンラインで販売する軽量なサンプル ストア実装で、基本的なサイト管理、ユーザー サインイン、ショッピング カート機能を実装しています。

このチュートリアル シリーズでは、ASP.NET MVC ミュージック ストア サンプル アプリケーションをビルドするために必要なすべての手順について詳しく説明します。 パート 4 では、モデルとデータ アクセスについて説明します。

ここまでは、コントローラーからビュー テンプレートに "ダミー データ" を渡してきただけです。 これで、実際のデータベースをフックする準備ができました。 このチュートリアルでは、データベース エンジンとして SQL Server Compact Edition (多くの場合、SQL CE と呼ばれます) を使用する方法について説明します。 SQL CE は無料で、インストールや構成を必要としない埋め込みのファイル ベースのデータベースであり、ローカル開発に非常に便利です。

Entity Framework Code-First を使用したデータベース アクセス

ASP.NET MVC 3 プロジェクトに含まれる Entity Framework (EF) サポートを使用して、データベースのクエリと更新を行います。 EF は柔軟なオブジェクト リレーショナル マッピング (ORM) データ API であり、開発者はオブジェクト指向の方法でデータベース内の格納データのクエリと更新を行うことができます。

Entity Framework バージョン 4 では、コード ファーストと呼ばれる開発パラダイムがサポートされています。 コード ファーストを使用すると、単純なクラス ("plain-old" CLR オブジェクトの POCO とも呼ばれます) を記述してモデル オブジェクトを作成でき、ご利用のクラスからその場でデータベースを作成することもできます。

モデル クラスの変更点

このチュートリアルでは、Entity Framework のデータベース作成機能を利用します。 しかし、その前に、モデル クラスにいくつかのマイナー変更を加えて、後で使用する内容をいくつか追加しましょう。

Artist モデル クラスの追加

アルバムはアーティストに関連付けられるので、アーティストを説明する単純なモデル クラスを追加します。 以下のコードを使用して、Artist.cs という名前の Models フォルダーに新しいクラスを追加します。

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] を選択します。

Screenshot of the Project menu to add the A S P . N E T Folder to select the App Data.

web.config ファイルでの接続文字列の作成

Entity Framework がデータベースへの接続方法を認識できるように、Web サイトの構成ファイルに数行を追加します。 プロジェクトのルートにある Web.config ファイルをダブルクリックします。

Screenshot of the Web config file in the Solution explorer to create a connection string in it.

このファイルの一番下までスクロールし、次に示すように、最後の行のすぐ上に <connectionStrings> セクションを追加します。

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

コンテキスト クラスの追加

Models フォルダーを右クリックし、MusicStoreEntities.cs という名前の新しいクラスを追加します。

Screenshot of the Models folder to add a Context Class.

このクラスは 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 フォルダーにドロップします。

Screenshot of the Code or Models folder to locate the Sample Data C S file and add the store catalog data.

次に、その SampleData クラスについて Entity Framework に伝えるコードを 1 行追加する必要があります。 プロジェクトのルートにある 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 で、storeDB という名前の MusicStoreEntities クラスのインスタンスを保持するフィールドを宣言します。

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

データベースのクエリを実行するためのストア インデックスの更新

MusicStoreEntities クラスは Entity Framework によって保持され、データベース内の各テーブルのコレクション プロパティを公開します。 StoreController のインデックス アクションを更新して、データベース内のすべてのジャンルを取得してみましょう。 以前は、文字列データをハード コーディングすることでこれを行っていました。 代わりに、Entity Framework コンテキストの Generes コレクションを使用できるようになりました。

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

以前に返したのと同じ StoreIndexViewModel が引き続き返されるため、ビュー テンプレートに変更を加える必要はありません。今はデータベースからライブ データを返しているだけです。

プロジェクトをもう一度実行して "/Store" URL にアクセスすると、データベース内のすべてのジャンルの一覧が表示されます。

Screenshot of the list of all Genres in the database.

ライブ データを使用するための Store Browse と Details の更新

/Store/Browse?genre=[some-genre] アクション メソッドを使用して、名前でジャンルを検索しています。 同じジャンル名に対して 2 つのエントリを持つべきではないので、1 つの結果しか期待できません。そのため、LINQ の .Single() 拡張機能を使用して、次のように適切な Genre オブジェクトのクエリを実行します (まだ入力しないでください)。

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

Single メソッドは、ラムダ式をパラメーターとして受け取ります。これは、定義した値と名前が一致するように、1 つの Genre オブジェクトが必要であることを指定します。 上記の場合は、Disco に一致する Name 値を持つ 1 つの Genre オブジェクトを読み込みます。

Entity Framework 機能を利用して、Genre オブジェクトを取得したときに読み込みたい他の関連エンティティも示すことができます。 この機能はクエリ結果の構造化と呼ばれ、必要なすべての情報を取得するためにデータベースにアクセスする必要がある回数を減らすことができます。 取得した Genre の Albums をプリフェッチする必要があるため、Genres.Include("Albums") から含め、関連するアルバムも必要であることを示すようにクエリを更新します。 これは、単一データベースの要求で Genre データと Album データの両方を取得するため、より効率的です。

説明が終わったところで、Browse コントローラー アクションがどのようになるかを次に示します。

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);
}

Store Browse ビューを更新して、各ジャンルで利用可能なアルバムを表示できるようになりました。 ビュー テンプレート (/Views/Store/Browse.cshtml にあります) を開き、次に示すように Albums の箇条書きを追加します。

@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 を参照すると、結果がデータベースから取得され、選択した Genre 内のすべてのアルバムが表示されます。

Screenshot of the results when pulled from the database displays all albums in the selected Genre.

/Store/Details/[id] URL に同じ変更を加え、ID がパラメーター値と一致する Album を読み込むデータベース クエリでダミー データを置き換えます。

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

アプリケーションを実行して /Store/Details/1 を参照すると、結果がデータベースからプルされるようになっています。

Screenshot of the Store Details page shows that the results are also being pulled from the database.

Store Details ページが Album ID でアルバムを表示するように設定されたので、[参照] ビューを更新して Details ビューにリンクしましょう。 前のセクションの最後に Store Index から Store Browse へのリンクを行ったのとまったく同じように、Html.ActionLink を使用します。 Browse ビューの完全なソースが次のとおりです。

@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>

Store ページから利用可能なアルバムを一覧表示する Genre ページまで参照できるようになりました。アルバムをクリックすると、そのアルバムの詳細を表示できます。

Screenshot of being able to browse from the Store page to a Genre page.