パート 4、ASP.NET Core MVC アプリにモデルを追加する

作成者: Rick Anderson および Jon P Smith

このチュートリアルでは、データベースの映画を管理するためのクラスが追加されます。 これらのクラスは、MVC アプリの "デル" 部分です。

これらのモデル クラスは、データベースを操作するために Entity Framework Core (EF Core) で使用されます。 EF Core は、記述する必要があるデータ アクセス コードを簡略化するオブジェクト リレーショナル マッピング (ORM) フレームワークです。

作成されたモデル クラスは、Plain Old CLR Objects の頭文字を取って、POCO クラスと呼ばれます。 POCO クラスには EF Core に対する依存関係はありません。 これらは、データベースに格納されるデータのプロパティのみを定義します。

このチュートリアルでは、まずモデル クラスが作成され、EF Core によってデータベースが作成されます。

データ モデル クラスの追加

Models フォルダーを右クリックし、[追加]>[クラス] の順に選択します。 そのファイルに Movie.cs という名前を付けます。

Models/Movie.cs ファイルを次のコードで更新します。

using System.ComponentModel.DataAnnotations;

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

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie クラスには、データベースで主キー用に必要となる Id フィールドが含まれています。

ReleaseDateDataType 属性により、データの型 (Date) が指定されます。 この属性を使用する場合:

  • ユーザーは日付フィールドに時刻の情報を入力する必要はありません。
  • 日付のみが表示され、時刻の情報は表示されません。

DataAnnotations は、後のチュートリアルで説明されます。

string の後の疑問符は、そのプロパティが Null 許容であることを示します。 詳細については、「null 許容参照型」を参照してください。

NuGet パッケージを追加する

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] (PMC) の順に選択します。

PMC メニュー

PMC で次のコマンドを実行します。

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

上記のコマンドにより次が追加されます。

  • EF Core SQL Server プロバイダー。 プロバイダー パッケージによって、依存関係として EF Core パッケージがインストールされます。
  • パッケージによって使用されるユーティリティは、このチュートリアルの後半で、スキャフォールディングの手順で自動的にインストールされます。

コンパイラ エラーのチェックとしてプロジェクトをビルドします。

ムービー ページのスキャフォールディング

スキャフォールディング ツールを使用し、ムービー モデルの CreateReadUpdateDelete の (CRUD) のページを生成します。

ソリューション エクスプローラーで、Controllers フォルダーを右クリックし、[追加] > [新規スキャフォールディング アイテム] の順に選択します。

前述の手順を参照

[スキャフォールディングを追加] ダイアログで、[Entity Framework を使用したビューがある MVC コントローラー] > [追加] の順に選択します。

[スキャフォールディングを追加] ダイアログ

[Entity Framework を使用したビューがある MVC コントローラーを追加する] ダイアログを完了します。

  • [モデル クラス] ドロップ ダウンで、 [Movie (MvcMovie.Models)] を選択します。
  • [データ コンテキスト クラス] 行で、[+] (プラス) 記号を選択します。
    • [データ コンテキストの追加] ダイアログで、クラス名 MvcMovie.Data.MvcMovieContext が生成されます。
    • [追加] を選択します。
  • ビューコントローラー名: 既定値のままにします。
  • [追加] を選択します。

[データの追加] コンテキストで既定値のままにします

エラー メッセージが表示された場合は、もう一度 [追加] を選択して再試行します。

スキャフォールディングによって次が更新されます。

  • 必要なパッケージ参照を MvcMovie.csproj プロジェクト ファイルに挿入します。
  • Program.cs ファイルでデータベース コンテキストを登録します。
  • データベース接続文字列を appsettings.json ファイルに追加します。

スキャフォールディングによって次が作成されます。

  • ムービー コントローラー: Controllers/MoviesController.cs
  • 作成削除詳細編集、およびインデックス ページ用の Razor ビュー ファイル: Views/Movies/*.cshtml
  • データベース コンテキスト クラス: Data/MvcMovieContext.cs

これらのファイルとファイル更新の自動作成は、"スキャフォールディング" と呼ばれます。

データベースが存在しないため、スキャフォールディング ページをまだ使用できません。 アプリを実行し、 [Movie App] リンクを選択すると、 [データベースを開けません] または [そのようなテーブルはありません: Movie] エラー メッセージが表示されます。

アプリのビルド

アプリをビルドします。 コンパイラにより、null 値の処理方法に関するいくつかの警告が生成されます。 詳細については、こちらの GitHub のイシューと「null 許容参照型」を参照してください。

null 許容参照型から警告を除去するには、MvcMovie.csproj ファイルから次の行を削除します。

<Nullable>enable</Nullable>

次のリリースでこの問題を解決する予定です。

最初の移行

EF Core 移行機能を使用し、データベースを作成します。 "移行" は、データ モデルに合わせてデータベースを作成および更新する一連のツールです。

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] の順に選択します。

パッケージ マネージャー コンソール (PMC) で、次のコマンドを入力します。

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:Migrations/{timestamp}_InitialCreate.cs 移行ファイルが生成されます。 InitialCreate 引数は、移行の名前です。 任意の名前を使用できますが、慣例により、移行を説明する名前が選択されます。 これは最初の移行であるため、生成されたクラスには、データベース スキーマを作成するコードが含まれています。 データベース スキーマは、MvcMovieContext クラスで指定されたモデルに基づきます。

  • Update-Database:前のコマンドで作成された最新の移行にデータベースを更新します。 このコマンドにより Migrations/{time-stamp}_InitialCreate.cs ファイルで Up メソッドが実行され、データベースが作成されます。

Update-Database コマンドでは、以下の警告が生成されます。

エンティティ型 'Movie' の decimal 列 'Price' に型が指定されていません。 これにより、値が既定の有効桁数と小数点以下桁数に収まらない場合、自動的に切り捨てられます。 'HasColumnType()' を使用してすべての値に適合する SQL server 列の型を明示的に指定します。

前の警告は無視してください。これは、後のチュートリアルで修正されます。

EF Core 用の PMC ツールの詳細については、EF Core ツール リファレンスの Visual Studio の PMC に関するページを参照してください。

アプリをテストする

アプリを実行し、 [Movie App] リンクを選択します。

次のような例外が表示された場合は、移行手順を実行していなかった可能性があります。

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使う英語以外のロケール、および英語 (米国) 以外の日付形式で、jQuery 検証をサポートするには、アプリをグローバル化する必要があります。 グローバル化の手順については、この GitHub の記事をご覧ください。

生成されたデータベース コンテキスト クラスと登録を調べる

EF Core では、データ アクセスはモデルを利用して実行されます。 モデルはエンティティ クラスと、データベースとのセッションを表すコンテキスト オブジェクトから構成されます。 このコンテキスト オブジェクトにより、データのクエリと保存が可能になります。 データベース コンテキストは Microsoft.EntityFrameworkCore.DbContext から派生し、データ モデルに含めるエンティティを指定します。

スキャフォールディングにより、Data/MvcMovieContext.cs データベース コンテキスト クラスが作成されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

上記のコードによって、データベース内のムービーを表す DbSet<Movie> プロパティが作成されます。

依存関係の挿入

ASP.NET Core には、依存関係挿入 (DI) が組み込まれています。 データベース コンテキストなどのサービスは、Program.cs の DI に登録されます。 これらのサービスは、それを必要とするコンポーネントにコンストラクター パラメーターを介して提供されます。

Controllers/MoviesController.cs ファイルでは、コンストラクターが依存性の注入を使って MvcMovieContext データベース コンテキストがコントローラーに挿入されています。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。

スキャフォールディングによって、Program.cs で次の強調表示されたコードが生成されました。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

ASP.NET Core 構成システムによって "MvcMovieContext" データベース接続文字列が読み取られます。

生成されたデータベース接続文字列を調べる

スキャフォールディングによって、接続文字列が appsettings.json ファイルに追加されました。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5b790-765f-4381-988c-5167405bb107;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

ローカル開発の場合、ASP.NET Core 構成システムによって appsettings.json ファイルから ConnectionString キーが読み取られます。

InitialCreate クラス

Migrations/{timestamp}_InitialCreate.cs 移行ファイルを調べます。

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

上のコードでは以下の操作が行われます。

  • InitialCreate.Up により Movie テーブルが作成され、主キーとして Id が構成されます。
  • InitialCreate.Down により、Up 移行で行われたスキーマ変更が元に戻ります。

コントローラーの依存関係挿入

Controllers/MoviesController.cs ファイルを開いて、コンストラクターを調べます。

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

コンストラクターでは、依存性の注入を使ってデータベース コンテキスト (MvcMovieContext) がコントローラーに挿入されています。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。

Create ページをテストします。 データを入力して送信します。

[編集][詳細][削除] の各ページをテストします。

厳密に型指定されたモデルと @model ディレクティブ

コントローラーで ViewData ディクショナリを使ってビューにデータまたはオブジェクトを渡す方法を前に示しました。 ViewData ディクショナリは動的オブジェクトであり、ビューに情報を渡すための便利な遅延バインディングの方法を提供します。

MVC には、厳密に型指定されたモデル オブジェクトをビューに渡す機能があります。 この厳密に型指定された方法では、コンパイル時にコードを確認できます。 スキャフォールディング メカニズムでは、 MoviesController クラスとビューで、型指定されたモデルを渡しました。

Controllers/MoviesController.cs ファイルで生成された 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 パラメーターはルート データとして渡されます。 たとえば、https://localhost:5001/movies/details/1 は次のように設定します。

  • コントローラーを movies コントローラーに (最初の URL セグメント)。
  • アクションを details に (2 番目の URL セグメント)。
  • id を 1 に (最後の URL セグメント)。

id は、次の例で示すようにクエリ文字列で渡すことができます。

https://localhost:5001/movies/details?id=1

id 値が指定されていない場合、id パラメーターは null 許容型 (int?) として定義されます。

ルート データまたはクエリ文字列の値と一致するムービー エンティティを選択するため、ラムダ式FirstOrDefaultAsync メソッドに渡されます。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

ムービーが見つかった場合、Movie モデルのインスタンスが Details ビューに渡されます。

return View(movie);

Views/Movies/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.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </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.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </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 ステートメントにより、ビューで求められるオブジェクトの型が指定されます。 ムービー コントローラーが作成されたとき、次の @model ステートメントが含まれました。

@model MvcMovie.Models.Movie

この @model ディレクティブにより、コントローラーでビューに渡されたムービーにアクセスできます。 Model オブジェクトは厳密に型指定されます。 たとえば、Details.cshtml ビューでは、コードで厳密に型指定された Model オブジェクトを使って、DisplayNameForDisplayFor の HTML ヘルパーに各ムービー フィールドを渡しています。 Create および Edit のメソッドとビューも、Movie モデル オブジェクトを渡します。

Movies コントローラーの Index.cshtml ビューと Index メソッドを確認します。 コードで View メソッドを呼び出すときの List オブジェクトの作成方法に注意してください。 コードでは、この Movies リストを Index アクション メソッドからビューに渡しています。

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

ムービー コントローラーが作成されたとき、スキャフォールディングにより、Index.cshtml ファイルの一番上に次の @model ステートメントが含まれました。

@model IEnumerable<MvcMovie.Models.Movie>

@model ディレクティブにより、厳密に型指定された Model オブジェクトを使って、コントローラーがビューに渡したムービーのリストにアクセスできます。 たとえば、Index.cshtmlIndex.cshtmlforeach ビューのコードでは、foreach ステートメントを使って厳密に型指定された Model オブジェクトのムービーをループ処理しています。

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Model オブジェクトは IEnumerable<Movie> オブジェクトとして厳密に型指定されているので、ループ内の各項目は Movie として型指定されます。 それ以外の利点として、コンパイラはコードで使用されている型を検証します。

その他の技術情報

データ モデル クラスの追加

Models フォルダーを右クリックし、[追加]>[クラス] の順に選択します。 そのファイルに Movie.cs という名前を付けます。

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

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie クラスには、データベースで主キー用に必要となる Id フィールドが含まれています。

ReleaseDateDataType 属性により、データの型 (Date) が指定されます。 この属性を使用する場合:

  • ユーザーは日付フィールドに時刻の情報を入力する必要はありません。
  • 日付のみが表示され、時刻の情報は表示されません。

DataAnnotations は、後のチュートリアルで説明されます。

NuGet パッケージを追加する

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] (PMC) の順に選択します。

PMC メニュー

PMC で次のコマンドを実行します。

Install-Package Microsoft.EntityFrameworkCore.Design

上記のコマンドにより次が追加されます。

  • EF Core SQL Server プロバイダー。 プロバイダー パッケージによって、依存関係として EF Core パッケージがインストールされます。
  • パッケージによって使用されるユーティリティは、このチュートリアルの後半で、スキャフォールディングの手順で自動的にインストールされます。

コンパイラ エラーのチェックとしてプロジェクトをビルドします。

ムービー ページのスキャフォールディング

スキャフォールディング ツールを使用し、ムービー モデルの CreateReadUpdateDelete の (CRUD) のページを生成します。

ソリューション エクスプローラーで、Controllers フォルダーを右クリックし、[追加] > [新規スキャフォールディング アイテム] の順に選択します。

前述の手順を参照

[スキャフォールディングを追加] ダイアログで、[Entity Framework を使用したビューがある MVC コントローラー] > [追加] の順に選択します。

[スキャフォールディングを追加] ダイアログ

[Entity Framework を使用したビューがある MVC コントローラーを追加する] ダイアログを完了します。

  • [モデル クラス] ドロップ ダウンで、 [Movie (MvcMovie.Models)] を選択します。
  • [データ コンテキスト クラス] 行で、[+] (プラス) 記号を選択します。
    • [データ コンテキストの追加] ダイアログで、クラス名 MvcMovie.Data.MvcMovieContext が生成されます。
    • [追加] を選択します。
  • ビューコントローラー名: 既定値のままにします。
  • [追加] を選択します。

[データの追加] コンテキストで既定値のままにします

スキャフォールディングによって次が更新されます。

  • 必要なパッケージ参照を MvcMovie.csproj プロジェクト ファイルに挿入します。
  • Startup.cs ファイルの Startup.ConfigureServices でデータベース コンテキストを登録します。
  • データベース接続文字列を appsettings.json ファイルに追加します。

スキャフォールディングによって次が作成されます。

  • ムービー コントローラー: Controllers/MoviesController.cs
  • 作成削除詳細編集、およびインデックス ページ用の Razor ビュー ファイル: Views/Movies/*.cshtml
  • データベース コンテキスト クラス: Data/MvcMovieContext.cs

これらのファイルとファイル更新の自動作成は、スキャフォールディングと呼ばれます。

データベースが存在しないため、スキャフォールディング ページをまだ使用できません。 アプリを実行し、 [Movie App] リンクを選択すると、 [データベースを開けません] または [そのようなテーブルはありません: Movie] エラー メッセージが表示されます。

最初の移行

EF Core 移行機能を使用し、データベースを作成します。 移行は、データ モデルに合わせてデータベースを作成および更新する一連のツールです。

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] の順に選択します。

パッケージ マネージャー コンソール (PMC) で、次のコマンドを入力します。

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:Migrations/{timestamp}_InitialCreate.cs 移行ファイルが生成されます。 InitialCreate 引数は、移行の名前です。 任意の名前を使用できますが、慣例により、移行を説明する名前が選択されます。 これは最初の移行であるため、生成されたクラスには、データベース スキーマを作成するコードが含まれています。 データベース スキーマは、MvcMovieContext クラスで指定されたモデルに基づきます。

  • Update-Database:前のコマンドで作成された最新の移行にデータベースを更新します。 このコマンドにより Migrations/{time-stamp}_InitialCreate.cs ファイルで Up メソッドが実行され、データベースが作成されます。

Update-Database コマンドでは、以下の警告が生成されます。

エンティティ型 'Movie' の decimal 列 'Price' に型が指定されていません。 これにより、値が既定の有効桁数と小数点以下桁数に収まらない場合、自動的に切り捨てられます。 'HasColumnType()' を使用してすべての値に適合する SQL server 列の型を明示的に指定します。

前の警告は無視してください。これは、後のチュートリアルで修正されます。

EF Core 用の PMC ツールの詳細については、EF Core ツール リファレンスの Visual Studio の PMC に関するページを参照してください。

アプリをテストする

アプリを実行し、 [Movie App] リンクを選択します。

次のような例外が表示された場合は、移行手順を実行していなかった可能性があります。

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使う英語以外のロケール、および英語 (米国) 以外の日付形式で、jQuery 検証をサポートするには、アプリをグローバル化する必要があります。 グローバル化の手順については、この GitHub の記事をご覧ください。

生成されたデータベース コンテキスト クラスと登録を調べる

EF Core では、データ アクセスはモデルを利用して実行されます。 モデルはエンティティ クラスと、データベースとのセッションを表すコンテキスト オブジェクトから構成されます。 このコンテキスト オブジェクトにより、データのクエリと保存が可能になります。 データベース コンテキストは Microsoft.EntityFrameworkCore.DbContext から派生し、データ モデルに含めるエンティティを指定します。

スキャフォールディングにより、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; }
    }
}

上記のコードによって、データベース内のムービーを表す DbSet<Movie> プロパティが作成されます。

ASP.NET Core には、依存関係挿入 (DI) が組み込まれています。 データベース コンテキストなどのサービスは、Startup の DI に登録する必要があります。 これらのサービスを必要とするコンポーネントは、コンストラクター パラメーターを介して提供されます。

Controllers/MoviesController.cs ファイルでは、コンストラクターが依存性の注入を使って MvcMovieContext データベース コンテキストがコントローラーに挿入されています。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。

スキャフォールディングによって、Startup.ConfigureServices で次の強調表示されたコードが生成されました。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

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

ASP.NET Core 構成システムによって "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"
  }
}

ローカル開発の場合、ASP.NET Core 構成システムによって appsettings.json ファイルから ConnectionString キーが読み取られます。

InitialCreate クラス

Migrations/{timestamp}_InitialCreate.cs 移行ファイルを調べます。

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

上のコードでは以下の操作が行われます。

  • InitialCreate.Up により Movie テーブルが作成され、主キーとして Id が構成されます。
  • InitialCreate.Down により、Up 移行で行われたスキーマ変更が元に戻ります。

コントローラーの依存関係挿入

Controllers/MoviesController.cs ファイルを開いて、コンストラクターを調べます。

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

コンストラクターでは、依存性の注入を使ってデータベース コンテキスト (MvcMovieContext) がコントローラーに挿入されています。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。

Create ページをテストします。 データを入力して送信します。

[編集][詳細][削除] の各ページをテストします。

厳密に型指定されたモデルと @model ディレクティブ

コントローラーで ViewData ディクショナリを使ってビューにデータまたはオブジェクトを渡す方法を前に示しました。 ViewData ディクショナリは動的オブジェクトであり、ビューに情報を渡すための便利な遅延バインディングの方法を提供します。

MVC には、厳密に型指定されたモデル オブジェクトをビューに渡す機能があります。 この厳密に型指定された方法では、コンパイル時にコードを確認できます。 スキャフォールディング メカニズムでは、 MoviesController クラスとビューで、型指定されたモデルを渡しました。

Controllers/MoviesController.cs ファイルで生成された 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 パラメーターはルート データとして渡されます。 たとえば、https://localhost:5001/movies/details/1 は次のように設定します。

  • コントローラーを movies コントローラーに (最初の URL セグメント)。
  • アクションを details に (2 番目の URL セグメント)。
  • id を 1 に (最後の URL セグメント)。

id は、次の例で示すようにクエリ文字列で渡すことができます。

https://localhost:5001/movies/details?id=1

id 値が指定されていない場合、id パラメーターは null 許容型 (int?) として定義されます。

ルート データまたはクエリ文字列の値と一致するムービー エンティティを選択するため、ラムダ式FirstOrDefaultAsync メソッドに渡されます。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

ムービーが見つかった場合、Movie モデルのインスタンスが Details ビューに渡されます。

return View(movie);

Views/Movies/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.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </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.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </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 ステートメントにより、ビューで求められるオブジェクトの型が指定されます。 ムービー コントローラーが作成されたとき、次の @model ステートメントが含まれました。

@model MvcMovie.Models.Movie

この @model ディレクティブにより、コントローラーでビューに渡されたムービーにアクセスできます。 Model オブジェクトは厳密に型指定されます。 たとえば、Details.cshtml ビューでは、コードで厳密に型指定された Model オブジェクトを使って、DisplayNameForDisplayFor の HTML ヘルパーに各ムービー フィールドを渡しています。 Create および Edit のメソッドとビューも、Movie モデル オブジェクトを渡します。

Movies コントローラーの Index.cshtml ビューと Index メソッドを確認します。 コードで View メソッドを呼び出すときの List オブジェクトの作成方法に注意してください。 コードでは、この Movies リストを Index アクション メソッドからビューに渡しています。

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

ムービー コントローラーが作成されたとき、スキャフォールディングにより、Index.cshtml ファイルの一番上に次の @model ステートメントが含まれました。

@model IEnumerable<MvcMovie.Models.Movie>

@model ディレクティブにより、厳密に型指定された Model オブジェクトを使って、コントローラーがビューに渡したムービーのリストにアクセスできます。 たとえば、Index.cshtmlIndex.cshtmlforeach ビューのコードでは、foreach ステートメントを使って厳密に型指定された Model オブジェクトのムービーをループ処理しています。

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Model オブジェクトは IEnumerable<Movie> オブジェクトとして厳密に型指定されているので、ループ内の各項目は Movie として型指定されます。 それ以外の利点として、コンパイラはコードで使用されている型を検証します。

Entity Framework Core の SQL ログ

一般的に、ログの構成は appsettings.{Environment}.json ファイルの Logging セクションで指定されます。 SQL ステートメントをログに記録するには、appsettings.Development.json ファイルに "Microsoft.EntityFrameworkCore.Database.Command": "Information" を追加します。

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

上記の JSON では、SQL ステートメントがコマンド ラインと Visual Studio 出力ウィンドウに表示されます。

詳細については、「.NET Core および ASP.NET Core でのログ記録」およびこの GitHub イシューを参照してください。

その他の技術情報

データ モデル クラスの追加

Models フォルダーを右クリックし、[追加]>[クラス] の順に選択します。 そのファイルに Movie.cs という名前を付けます。

Movie.cs ファイルを次のコードで更新します。

using System;
using System.ComponentModel.DataAnnotations;

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

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie クラスには、データベースで主キー用に必要となる Id フィールドが含まれています。

ReleaseDateDataType 属性により、データの型 (Date) が指定されます。 この属性を使用する場合:

  • ユーザーは日付フィールドに時刻の情報を入力する必要はありません。
  • 日付のみが表示され、時刻の情報は表示されません。

DataAnnotations は、後のチュートリアルで説明されます。

NuGet パッケージを追加する

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] (PMC) の順に選択します。

PMC メニュー

PMC で次のコマンドを実行します。

Install-Package Microsoft.EntityFrameworkCore.SqlServer

上記のコマンドによって EF Core SQL Server プロバイダーが追加されます。 プロバイダー パッケージによって、依存関係として EF Core パッケージがインストールされます。 追加のパッケージは、このチュートリアルの後半で、スキャフォールディングの手順で自動的にインストールされます。

データベース コンテキスト クラスを作成する

データベース コンテキスト クラスは、Movie モデルの EF Core 機能 (作成、読み取り、更新、削除) を調整するために必要です。 データベース コンテキストは Microsoft.EntityFrameworkCore.DbContext から派生し、データ モデルに含めるエンティティを指定します。

Data フォルダーを作成します。

次のコードを含む 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; }
    }
}

上記のコードによって、エンティティ セットの DbSet<Movie> プロパティが作成されます。 Entity Framework の用語では、エンティティ セットは通常はデータベース テーブルに対応します。 エンティティはテーブル内の行に対応します。

データベース コンテキストの登録

ASP.NET Core には、依存関係挿入 (DI) が組み込まれています。 サービス (EF Core DB コンテキストなど) は、アプリケーションの起動時に DI に登録する必要があります。 これらのサービスを必要とするコンポーネント (Razor Pages など) は、コンストラクターのパラメーターを介して指定されます。 DB コンテキスト インスタンスを取得するコンストラクター コードは、チュートリアルの後半で示します。 このセクションでは、DI コンテナーにデータベース コンテキストを登録します。

Startup.cs の先頭に次の using ステートメントを追加します。

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Startup.ConfigureServices で次の強調表示されたコードを追加します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

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

DbContextOptions オブジェクトでメソッドが呼び出され、接続文字列の名前がコンテキストに渡されます。 ローカル開発の場合、ASP.NET Core 構成システムによって、appsettings.json ファイルから接続文字列が読み取られます。

データベース接続文字列を調べる

接続文字列を 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"
  }
}

コンパイラ エラーのチェックとしてプロジェクトをビルドします。

ムービー ページのスキャフォールディング

スキャフォールディング ツールを使用し、ムービー モデルの作成、読み取り、更新、削除の (CRUD) ページを生成します。

ソリューション エクスプローラーで、Controllers フォルダーを右クリックし、[追加] > [新規スキャフォールディング アイテム] の順に選択します。

前述の手順を参照

[スキャフォールディングを追加] ダイアログで、[Entity Framework を使用したビューがある MVC コントローラー] > [追加] の順に選択します。

[スキャフォールディングを追加] ダイアログ

[コントローラーの追加] ダイアログ ボックスを完了します。

  • [Model class](モデル クラス):Movie (MvcMovie.Models)
  • データ コンテキスト クラス:MvcMovieContext (MvcMovie.Data)

[データの追加] コンテキスト

  • ビュー: 各オプションの既定値をオンにします。
  • コントローラー名: 既定の MoviesController のままにします。
  • [追加] を選択します。

Visual Studio では、次が作成されます。

  • ムービー コントローラー (Controllers/MoviesController.cs)
  • 作成、削除、詳細、編集、およびインデックス ページ用の Razor ビュー ファイル (*Views/Movies/`.cshtml`)

このようなファイルの自動作成は、"スキャフォールディング" と呼ばれます。

データベースが存在しないため、スキャフォールディング ページをまだ使用できません。 アプリを実行し、 [Movie App] リンクをクリックすると、 [データベースを開けません] または [そのようなテーブルはありません:Movie] というエラー メッセージが表示されます。

最初の移行

EF Core 移行機能を使用し、データベースを作成します。 移行は、データ モデルに合わせてデータベースを作成したり、更新したりできる一連のツールです。

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] (PMC) の順に選択します。

PMC で、次のコマンドを入力します。

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate:Migrations/{timestamp}_InitialCreate.cs 移行ファイルが生成されます。 InitialCreate 引数は、移行の名前です。 任意の名前を使用できますが、慣例により、移行を説明する名前が選択されます。 これは最初の移行であるため、生成されたクラスには、データベース スキーマを作成するコードが含まれています。 データベース スキーマは、MvcMovieContext クラスで指定されたモデルに基づきます。

  • Update-Database:前のコマンドで作成された最新の移行にデータベースを更新します。 このコマンドにより Migrations/{time-stamp}_InitialCreate.cs ファイルで Up メソッドが実行され、データベースが作成されます。

    データベース更新コマンドにより、次の警告が生成されます。

    エンティティ型 'Movie' の decimal 列 'Price' に型が指定されていません。 これにより、値が既定の有効桁数と小数点以下桁数に収まらない場合、自動的に切り捨てられます。 'HasColumnType()' を使用してすべての値に適合する SQL server 列の型を明示的に指定します。

    この警告は無視して構いません。後のチュートリアルで修正されます。

EF Core 用の PMC ツールの詳細については、EF Core ツール リファレンスの Visual Studio の PMC に関するページを参照してください。

InitialCreate クラス

Migrations/{timestamp}_InitialCreate.cs 移行ファイルを調べます。

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", 
                                 SqlServerValueGenerationStrategy.IdentityColumn),
                Title = table.Column<string>(nullable: true),
                ReleaseDate = table.Column<DateTime>(nullable: false),
                Genre = table.Column<string>(nullable: true),
                Price = table.Column<decimal>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

Up メソッドにより Movie テーブルが作成され、主キーとして Id が構成されます。 Down メソッドにより、Up 移行で行われたスキーマ変更が元に戻ります。

アプリのテスト

  • アプリを実行し、 [Movie App] リンクをクリックします。

    次のような例外が表示された場合:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

移行手順を実行しなかった可能性があります。

  • Create ページをテストします。 データを入力して送信します。

    Note

    Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使う英語以外のロケール、および英語 (米国) 以外の日付形式で、jQuery 検証をサポートするには、アプリをグローバル化する必要があります。 グローバル化の手順については、この GitHub の記事をご覧ください。

  • [編集][詳細][削除] の各ページをテストします。

コントローラーの依存関係挿入

Controllers/MoviesController.cs ファイルを開いて、コンストラクターを調べます。

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

コンストラクターでは、依存性の注入を使ってデータベース コンテキスト (MvcMovieContext) がコントローラーに挿入されています。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。

厳密に型指定されたモデルと @model キーワード

コントローラーで ViewData ディクショナリを使ってビューにデータまたはオブジェクトを渡す方法を前に示しました。 ViewData ディクショナリは動的オブジェクトであり、ビューに情報を渡すための便利な遅延バインディングの方法を提供します。

MVC にも、厳密に型指定されたモデル オブジェクトをビューに渡す機能があります。 この厳密に型指定された方法では、コンパイル時にコードを確認できます。 スキャフォールディング メカニズムでは、MoviesController のクラスとビューで、この手法 (つまり、厳密に型指定されたモデルを渡しました) が使用されました。

Controllers/MoviesController.cs ファイルで生成された 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 パラメーターはルート データとして渡されます。 たとえば、https://localhost:5001/movies/details/1 は次のように設定します。

  • コントローラーを movies コントローラーに (最初の URL セグメント)。
  • アクションを details に (2 番目の URL セグメント)。
  • ID を 1 に (最後の URL セグメント)。

次のようにクエリ文字列で id を渡すこともできます。

https://localhost:5001/movies/details?id=1

ID 値が指定されない場合、id パラメーターは null 許容型 (int?) として定義されます。

ルート データまたはクエリ文字列の値と一致するムービー エンティティを選択するため、ラムダ式FirstOrDefaultAsync に渡されます。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

ムービーが見つかった場合、Movie モデルのインスタンスが Details ビューに渡されます。

return View(movie);

Views/Movies/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.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </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.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </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 ステートメントにより、ビューで求められるオブジェクトの型が指定されます。 ムービー コントローラーが作成されたとき、次の @model ステートメントが含まれました。

@model MvcMovie.Models.Movie

この @model ディレクティブにより、コントローラーでビューに渡されたムービーにアクセスできます。 Model オブジェクトは厳密に型指定されます。 たとえば、Details.cshtml ビューでは、コードで厳密に型指定された Model オブジェクトを使って、DisplayNameForDisplayFor の HTML ヘルパーに各ムービー フィールドを渡しています。 Create および Edit のメソッドとビューも、Movie モデル オブジェクトを渡します。

Movies コントローラーの Index.cshtml ビューと Index メソッドを確認します。 コードで View メソッドを呼び出すときの List オブジェクトの作成方法に注意してください。 コードでは、この Movies リストを Index アクション メソッドからビューに渡しています。

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

ムービー コントローラーが作成されたとき、スキャフォールディングにより、Index.cshtml ファイルの一番上に次の @model ステートメントが含まれました。

@model IEnumerable<MvcMovie.Models.Movie>

@model ディレクティブにより、厳密に型指定された Model オブジェクトを使って、コントローラーがビューに渡したムービーのリストにアクセスできます。 たとえば、Index.cshtmlIndex.cshtmlforeach ビューのコードでは、foreach ステートメントを使って厳密に型指定された Model オブジェクトのムービーをループ処理しています。

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Model オブジェクトは厳密に型指定されているので (IEnumerable<Movie> オブジェクトとして)、ループ内の各項目は Movie として型指定されます。 これには、コンパイル時にコードを確認できるなどの利点があります。

その他の技術情報