ASP.NET Core MVCのチュートリアルを丁寧にやってみた②(モデルの追加とDBの作成)

ASP.NET Core

ASP.NET Core MVCの公式チュートリアルを丁寧に解説する記事の第2回です。

第1回では、プロジェクトの作成からコントローラーやビューの仕組みなどについて確認しました。

第2回では、MVCにおけるモデルを作成し、EntityFrameworkCoreを使ってDBと連携するまでを見ていきます。

公式チュートリアルではパート4~5の内容に該当します。

ASP.NET Core MVC の概要
ASP.NET Core MVC の概要について説明します。
スポンサーリンク

Entity Framework Coreを使ったモデルの追加

前回はMVCのビューとコントローラーを作成して、それらの基本的な動作について確認しました。

今回はアプリの中心となるロジックを担うモデルを、Entity Framework CoreというO/Rマッパーを使って追加します。

O/Rマッパー(ORM)とは、アプリケーションとデータベース間の差異を吸収することで、データベースへの接続や操作を簡単にしてくれる便利なツールです。

.NET CoreではEntity Framework Core(EFCore)が標準搭載されており、これを使うことでSQLを書くことなく、C#のコードだけでデータベースにアクセスできます。

EFCoreのインストール

まずはプロジェクトにEFCoreをインストールしましょう。
「ツール」→「NuGetパッケージマネージャー」→「パッケージマネージャーコンソール」でコンソールを開きます。

パッケージマネージャーコンソール画面で次のコマンドを実行すると、EFCoreがインストールされます。
※プロジェクトのフレームワークのバージョンによって、インストール可能なパッケージのバージョンが異なるのでご注意ください。(2022.3.7 コメントでご指摘をいただき修正しました。)

.NET 6の場合

Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Design

.NET 5の場合

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.14
Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.14

.NET Core 3.1の場合

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 3.1.22
Install-Package Microsoft.EntityFrameworkCore.Design -Version 3.1.22

データモデルクラスの定義

EFCoreがインストールできたら、データベースのテーブルの元となるデータモデルクラスを定義します。

「Models」フォルダを右クリック→「追加」→「クラス」で「Movie.cs」という名前のファイルを追加し、次のコードに書き換えます。

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Genre { get; set; }
        public decimal Revenue { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
    }
}

データモデルクラスのポイントは次の3点です。

  • クラス名とテーブル名を一致させる
  • 各プロパティとテーブルの列名と一致させる
  • Idプロパティがテーブルの主キーとなる

なお、[DataType]属性を付けることで表示される形式(型)を指定することができ、ここではDate(日付型)を指定しているので時刻は画面に表示されなくなります。

コンテキストクラスの作成と登録

単なるクラスであるデータモデルをデータベースに対応させるためには、コンテキストクラスというものを別途用意する必要があります。

プロジェクト直下に「Data」フォルダを作成し、その中に「MvcMovieContext.cs」というファイルを追加しましょう。

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<Movie> Movie { get; set; }
    }
}

作成したコンテキストクラスはアプリケーションで利用できるように登録する必要があります。Startup.csに以下の強調表示されたコードを追加することで、コンテキストを登録することができます。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

namespace MvcMovie
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            services.AddDbContext<MvcMovieContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
        }

    <以下略>

接続文字列の追加

さらに、データベースに接続するために必要な文字列をappsettings.jsonに追加します。

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
        "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
}

(参考)接続文字列の内容

  • Server:接続先のSQL Server。今回はVisual Studioに標準搭載されているLocalDBを指定
  • Database:データベース名を指定。存在しなければ指定した名前で作成する
  • Trusted_Connection:trueはWindows認証、falseはSQL Server認証
  • MultipleActiveResultSets:trueで1つの接続で複数の結果セットを取得することが可能になる

ここまで用意できたら、ソリューションのビルド(Ctrl+Shift+B)を行いましょう。ビルドしないと次のスキャフォールディングでエラーになってしまいます。

スキャフォールディング

スキャフォールディングとは、CRUD(Create, Read, Update, Delete)機能を備えたプログラム(メソッド)を自動で生成してくれる便利な機能です。

Controllersフォルダを右クリックして、「追加」→「新規スキャフォールディングアイテム」をクリックします。

「Entity Frameworkを使用したビューがあるMVCコントローラー」を追加します。

モデルクラスに先ほど作成した「Movie」クラスを、データコンテキストクラスに「MvcMovieContext」を選択して「追加」をクリックすると、スキャフォールディングが開始されます。

※もしスキャフォールディング実行時にエラーが発生した場合は、パッケージの再インストールを試してみてください。

スキャフォールディングが完了すると、以下の赤枠のファイルが自動で追加され、CRUD操作ができるようになります。

ただし現時点ではデータベースがまだ作成されていないため、CRUD操作を行おうとしてもエラーになります。

データベースを作成するには次のマイグレーションを行う必要があります。

マイグレーション

マイグレーションとは、データモデルクラスに合わせてデータベース(テーブル)を作成したり、更新することができる機能です。今回は初回なので、実行するとデータベースが作成されます。

「ツール」→「NuGetパッケージマネージャー」→「パッケージマネージャーコンソール」でコンソールを開き、以下のコマンドを実行してみましょう。

Add-Migration InitialCreate
Update-Database

Add-Migrationコマンドはデータモデルクラスからマイグレーションファイルを作成します。
InitialCreateはマイグレーションファイルの名前で、慣例でマイグレーションの中身を説明するような名前を付けます。

その後Update-Databaseコマンドを実行することで、マイグレーションファイルの内容がデータベースに反映されます。

なお実行後にこのような警告が出ますが、後で修正するので無視してOKです。

マイグレーションが成功するとプロジェクト直下にMigrationsフォルダが作成されます。
その中に「<日付>_InitialCreate.cs」というファイルが作成されていることを確認しておきましょう。

アプリを実行してCRUD操作を試す

それではF5でデバッグ実行して動作を確認してみましょう。

https://localhost:<ポート番号>/moviesにアクセスすると次のように表示されるはずです。

試しに「Create New」をクリックして、適当なデータを入力後、Createボタンを押してみます。

「Back to List」で戻ると、今登録した内容が表示されていることがわかります。

「Details」をクリックすると詳細画面が表示されます。

起動時のURLの変更

以後は今回作成したMoviesControllerにアクセスすることになるので、Startup.csを編集して起動時のURLを変更しておきましょう。

<省略>

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Movies}/{action=Index}/{id?}");
            });
        }
    }
}

コントローラーからビューにモデルを渡す流れを確認

第1回では、コントローラからビューに値を渡す際にViewDataを使用しましたが、スキャフォールディングで作られたファイルでは厳密に型指定されたモデルを渡しています。

具体的にどのようにコントローラーからビューにモデルが渡されているのかを確認してみましょう。

まずMoviesControllerのDetailsメソッドを見てみます。

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

最初に引数としてidを受け取り、FirstOrDefaultAsyncメソッドによって、受け取ったidとMovieテーブルのIdが一致する単一のレコードを取得します。

取得した結果をモデルクラスのモデル(Movie型の変数)に格納し、中身がnullでなければViewメソッドでモデルをコントローラに渡します。

次にモデルが渡されるDetails.cshtmlを見てみましょう。

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Revenue)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Revenue)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

一番上の@modelディレクティブで、このビューで使用するモデルを指定しています。こうすることでインテリセンスや型チェックが機能するようになります。

さらにDisplayNameForやDisplayForといったHTMLヘルパーを使うことで、モデルの型に適した形式でビューに表示することができます。
(例:bool型ならチェックボックスで表示、日付型なら入力欄にカレンダーウィジェットを表示させるなど)

ちなみに現時点ではMovie.csでDisplayName属性を何も指定していないのでそのままのプロパティ名が表示されますが、指定していればその名前がDisplayNameForで表示されることになります。

テーブルの中身を確認

最後に、今回作成されたLocalDBのテーブルを確認してみましょう。
「表示」→「SQL Server オブジェクト エクスプローラー」をクリックします。

エクスプローラーが開いたら、「(localdb)\MSSQLLocalDB~」→「データベース」→「MvcMovieContext-1」→「テーブル」とツリーを展開し、dbo.Movieを右クリックして「データの表示」をクリックします。

Movieテーブルの内容が表示され、先ほど登録したデータが反映されていることが確認できました。

まとめ

今回は色々なことをやったので、Entity Framework Coreを始めて使う際に必要な操作を簡単にまとめてみます。

  1. NuGetパッケージマネージャーからEFCoreをインストール
  2. テーブルの元となるデータモデルクラスを定義
  3. データモデルとDBを対応させるコンテキストクラスの作成とアプリへの登録
  4. DBへの接続文字列を設定ファイルに追加
  5. スキャフォールディングを実行してCRUD機能の自動生成
  6. マイグレーションを実行してDBの作成と反映を行う

これで基本的なCRUD機能を備えたアプリケーションができます。

※もしデータモデルクラスのプロパティを変更したり、追加した場合は再度マイグレーションを行う必要があります。

第3回では、ビューのタグヘルパーやコントローラーのPOSTリクエストの処理についてより詳しく見るとともに、新たに検索機能の追加を行っていきます。

【参考書籍】

著:出井 秀行
¥3,428 (2022/05/24 10:40時点 | Amazon調べ)

コメント

  1. 部長 より:

    凄く解りやすいです!!
    難解な公式サイトの掲載と入れ替えて欲しい位です(^^;
    秀でた解説の才能をお持ちで羨ましい限り。
    ここはどうなってるのだろう?という箇所も解説頂けており「おお!」と興奮ならが理解出来ました。

    本当にありがとうございます!

    お礼ついでに、補足情報を書き込ませて下さい。

    Install-Package Microsoft.EntityFrameworkCore.SqlServer
    の個所で、

    Install-Package : NU1202: パッケージ Microsoft.EntityFrameworkCore.Tools 6.0.2 は netcoreapp3.1 (.NETCoreApp,Version=v3.1) と互換性がありません。 パッケージ Microsoft.EntityFrameworkCore.Tools 6.0.2 がサポートするもの: net6.0 (.NETCoreApp,Version=v6.0)
    発生場所 行:1 文字:1

    となったので↓で解決させました。
    Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.12
    Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.12

    無印だとnet6.0を取得しに行くVisualStudio2022仕様になったのかも?

    • ひらひら より:

      とても嬉しいお言葉をありがとうございます!
      元々は自分の勉強メモのような形から書き始めたものですが、結果的にお役に立てたようで大変嬉しいです。
      今後もできるだけわかりやすい記事を書いていきますので、どうぞよろしくお願いします。

      補足情報についてもありがとうございます!
      バージョンを指定しないでパッケージをインストールすると.NET6用の最新バージョン(6.0.0~)がインストールされてしまい、ご指摘の通り互換性がないと怒られてしまいますね…
      記事の内容が最新の環境に対応しておらず、ご不便をおかけしてしまい申し訳ありませんでした。
      早速該当箇所にその旨を追記させていただきました。

タイトルとURLをコピーしました