ASP.NET Core 3.0 Razor Pages 事始め(2) - スキャフォールディングとDBマイグレーション


## 記事目次

1. [ASP.NET Core 3.0 Razor Pages 事始め(1) - はじめてのRazor Pagesアプリケーション](https://qiita.com/gushwell/items/6d8b801923b639d9ea2e)
2. [ASP.NET Core 3.0 Razor Pages 事始め(2) - スキャフォールディングとDBマイグレーション](https://qiita.com/gushwell/items/7eb76edf7006c0f02be4) <-- この記事
3. [ASP.NET Core 3.0 Razor Pages 事始め(3) - マイグレーションのやり直しとURLルーティング](https://qiita.com/gushwell/items/7ebe70f0d6adfd10ae12)
4. [ASP.NET Core 3.0 Razor Pages 事始め(4) - ページモデルとページハンドラ](https://qiita.com/gushwell/items/1deac26aaed945cc8631)
5. [ASP.NET Core 3.0 Razor Pages 事始め(5) - Postページハンドラとタグヘルパー](https://qiita.com/gushwell/items/daf901d4d76301dadea7)
6. [ASP.NET Core 3.0 Razor Pages 事始め(6) - データベースに初期値を設定する](https://qiita.com/gushwell/items/f7be44e668a6e168fc61)
7. [ASP.NET Core 3.0 Razor Pages 事始め(7) - Viewの変更とコンカレンシー例外処理](https://qiita.com/gushwell/items/913e8e95a0a79949af3c)
8. [ASP.NET Core 3.0 Razor Pages 事始め(8) - 検索機能の追加](https://qiita.com/gushwell/items/134930aba1d181100547)
9. [ASP.NET Core 3.0 Razor Pages 事始め(9) - ページに新しいフィールドを追加する](https://qiita.com/gushwell/items/a330e592f76efda13c42)
10. [ASP.NET Core 3.0 Razor Pages 事始め(10) - 検証機能の追加](https://qiita.com/gushwell/items/12e180ef129ea6a05757)


---

前回:[ASP.NET Core 3.0 Razor Pages 事始め(1)](https://qiita.com/gushwell/items/6d8b801923b639d9ea2e) の続きです。


今回はモデル(映画を管理するクラス)を追加し、新規追加、編集、削除、一覧のページを作成します。

# モデルの追加

まず、VS Codeのエクスプローラーの RAZORPAGEMOVIE を選択後、 [新しいフォルダー] アイコンをクリックし、Models フォルダーを作成します。

Modelsフォルダ を右クリックし、[新しいファイル] で、Movie.csを作成します。

`Movie`クラスを以下のように定義します。

```c#
using System;
using System.ComponentModel.DataAnnotations;


namespace RazorPagesMovie.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というプロパティがあります。これがデータベースのテーブルの主キーになります。

`ReleaseDate`プロパティには、 `[DataType(DataType.Date)]` という属性が付加されています。これは、時刻の情報は利用しないという意味です。
UI側では、日付のみの入力、表示となります。


Ctrl+Shift+Bで プロジェクトをビルドして、エラーがないことを確認します。

## Movie モデルの追加

RazorPagesMovie プロジェクトの下に、Data フォルダを作成します。

RazorPagesMovieContext.cs ファイルを作成し、以下のように入力します。

```c#
using Microsoft.EntityFrameworkCore;

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

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


`RazorPagesMovieContext`は、`Microsoft.EntityFrameworkCore.DbContext`から派生しています。

`Movie`は、エンティティセット（EntityFrameworkの用語）と呼ばれるプロパティで、データベースのテーブルに対応します。



## 接続文字列の追加


次に、`appsettings.json` ファイルに接続文字列を追加します。`"ConnectionStrings"` がその指定している個所です。

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MovieContext": "Data Source=MvcMovie.db"
  }
}
```

## NuGet パッケージと EF ツールの追加

次の コマンドを実行し、各種パッケージをインストールします。

```sh
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
```


これで、以下のものが追加されます。


- .NET Core CLI の Entity Framework Core ツール
- EF Core SQLite プロバイダー
- EF のスキャフォールディングに必要なパッケージ


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

Startup.cs の `ConfigureServices`メソッドの最後に、`services.AddDbContext`の呼び出しを追加します。

```c#
using RazorPagesMovie.Data;
using Microsoft.EntityFrameworkCore;

…

    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.AddRazorPages();

            services.AddDbContext<RazorPagesMovieContext>(options =>
                options.UseSqlite(Configuration.GetConnectionString("MovieContext")));
        }

```

プロジェクトをビルドして、コンパイル エラーがないことを確認します。


### Prgoram.cs を覗いてみる

ところで、このStartUp.csに定義されている`Startup`クラスは、どこで参照しているのでしょうか？ 調べたところ、Program.csで参照していました。

Program.csは、以下のようなコードになっています。

```c#
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;

namespace RazorPagesMovie
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
```


具体的に何をやっているのかまでは、このコードだけから正確には理解はできませんが、ホストと言われるインスタンスを生成し、起動しているようです。そして、ホストを生成する際には、初期処理を行うためのクラスが`Startup`クラスだよと、指定しているということですね。

何か特別の初期処理をやらせたいという時だけ、この`Program`クラスのコードをいじることになると思われます。


## Movieモデルのスキャフォールディング

次に、作成したMovieモデルから、Create, Edit, Delete, Details, Index の各ページを作成します。

まず、以下のコマンドを実行します。

```sh
dotnet tool install --global dotnet-aspnet-codegenerator
```

これで、aspnet-codegenerator スキャフォールディング ツールがインストールされます。なお、このツールの実行には、先ほどインストールしたパッケージが必要になります。

続けて、次のコマンドを実行し、コードを自動生成します。

```sh
dotnet aspnet-codegenerator razorpage -m Movie -dc RazorPagesMovieContext -udl -outDir Pages/Movies --referenceScriptLibraries
```

以下、その実行結果です。

```sh
$ dotnet aspnet-codegenerator razorpage -m Movie -dc RazorPagesMovieContext -udl -outDir Pages/Movies
Building project ...
Finding the generator 'razorpage'...
Running the generator 'razorpage'...
Attempting to compile the application in memory.
Attempting to figure out the EntityFramework metadata for the model and DbContext: 'Movie'
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 3.0.0 initialized 'RazorPagesMovieContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: None
Added Razor Page : /Pages/Movies/Create.cshtml
Added PageModel : /Pages/Movies/Create.cshtml.cs
Added Razor Page : /Pages/Movies/Edit.cshtml
Added PageModel : /Pages/Movies/Edit.cshtml.cs
Added Razor Page : /Pages/Movies/Details.cshtml
Added PageModel : /Pages/Movies/Details.cshtml.cs
Added Razor Page : /Pages/Movies/Delete.cshtml
Added PageModel : /Pages/Movies/Delete.cshtml.cs
Added Razor Page : /Pages/Movies/Index.cshtml
Added PageModel : /Pages/Movies/Index.cshtml.cs
RunTime 00:00:17.52
```

VSCodeのエクスプローラーを見ると、プロジェクトに新たなフォルダ、ファイルが追加されているのが確認できます。

```
Pages/Moveisフォルダー
  Create.cshtml
  Create.cshtml.cs
  Delete.cshtml
  Delete.cshtml.cs
  Details.cshtml
  Details.cshtml.cs
  Edit.cshtml
  Edit.cshtml.cs
  Index.cshtml
  Index.cshtml.cs
```

## 最初のDBマイグレーション

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

```sh
dotnet ef migrations add InitialCreate
dotnet ef database update
```


### `ef migrations add InitialCreate` コマンド

`ef migrations add InitialCreate` コマンドは、最初のデータベース スキーマを作成するコードを`Migrations`フォルダに生成します。

InitialCreate という名前は任意です。

このコマンドは、`RazorPagesMovieContext` 内の以下のコードをみて、どのようなテーブルを生成したらよいかを判断しています。

```c#
    public DbSet<RazorPagesMovie.Models.Movie> Movie { get; set; }
```


### `ef database update` コマンド

`ef database update` コマンドは、`Migrations`フォルダに作成された `<time-stamp>_InitialCreate.cs` ファイルの `Up` メソッドを実行します。 `Up`メソッドにより、データベースが作成されます。


`Up`メソッドをここに示します。

```c#
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                ID = table.Column<int>(nullable: false)
                    .Annotation("Sqlite:Autoincrement", true),
                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);
            });
    }
```

`ef database update` コマンドを実行すると、プロジェクトのあるフォルダに、MvcMovie.db というファイルが作成されているのが確認できます。


## アプリのテスト

F5キーでアプリを実行後、ブラウザの URLに /movies を追加してenterキーを押します。

これでMovies/index ページが表示されました。


![スクリーンショット 2019-10-24 21.35.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/7f0f59c3-de92-c726-815d-376f1d54f1a4.png "スクリーンショット 2019-10-24 21.35.02.png")


Create, Edit, Deleteのページが動作するかも確認します。

---

やっと、ここまで来ました。正直、かなり面倒臭いですね。可能ならば、Visual Studio IDE使ったほうがはるかに簡単だと思われます。


次回は、スキャフォールディングで生成されたRazorページについて、見ていこうと思います。