ASP.NET Core 3.0 Razor Pages 事始め(6) - データベースに初期値を設定する



## 記事目次

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 を使用する](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/razor-pages/sql?view=aspnetcore-3.0&tabs=visual-studio)をやっていこうと思います。


## データベース接続のための設定

Startup.cs の ConfigureServices メソッドを見てみます。

最初に実施したスキャフォールディングの操作で、ConfigureServices メソッドに以下の行が追加されています。

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

このコードにより、データベースコンテキストとして、`RazorPagesMovieContext` がアプリに組み込まれます。
ここでは、オプションでSQLiteを利用することを指定しています。

`Configuration.GetConnectionString("MovieContext")` で接続文字列を構成ファイル(appsettings.json)から取得しています。


これらのコードによって、`RazorPagesMovieContext`のインスタンスをランタイムが生成してくれます。
自身でインスタンスを生成する必要はありません。

## DbContextの利用

各Pageモデルでは、コンストラクタの引数で、このインスタンスを受け取ることができます。

例えば、Index.cshtml.csだと、以下のコードのように、コンストラクタで、`RazorPagesMovieContext`のインスタンスを受け取っています。

```c#
public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Models.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Models.RazorPagesMovieContext context)
    {
        _context = context;
    }
    ……
```



チュートリアルのページによると、

> テストサーバーまたは運用サーバーにデプロイされると、環境変数を使用して接続文字列を実際のデータベース サーバーに設定できます。

とのことです。この辺りは、ASP.NET MVC5とは異なりますね。
具体的にどうやるのか調べたいところですが、前回、前々回のように脇道にそれていると、なかなか進まないので、必要になった時に、このあたりも調べようと思います。



## データベースのシード

初期データをインサートする処理を作成します。そのため、Modelsフォルダに、`SeedData`という名前のクラスを新規作成します。

`SeedData`クラスのコードは以下のとおりです。

```c#
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using RazorPagesMovie.Data;

namespace RazorPagesMovie.Models
{
    public static class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var context = new RazorPagesMovieContext(
                serviceProvider.GetRequiredService<
                    DbContextOptions<RazorPagesMovieContext>>()))
            {
                // Look for any movies.
                if (context.Movies.Any())
                {
                    return;   // DB has been seeded
                }

                context.Movies.AddRange(
                    new Movie
                    {
                        Title = "When Harry Met Sally",
                        ReleaseDate = DateTime.Parse("1989-2-12"),
                        Genre = "Romantic Comedy",
                        Price = 7.99M
                    },

                    new Movie
                    {
                        Title = "Ghostbusters ",
                        ReleaseDate = DateTime.Parse("1984-3-13"),
                        Genre = "Comedy",
                        Price = 8.99M
                    },

                    new Movie
                    {
                        Title = "Ghostbusters 2",
                        ReleaseDate = DateTime.Parse("1986-2-23"),
                        Genre = "Comedy",
                        Price = 9.99M
                    },

                    new Movie
                    {
                        Title = "Rio Bravo",
                        ReleaseDate = DateTime.Parse("1959-4-15"),
                        Genre = "Western",
                        Price = 3.99M
                    }
                );
                context.SaveChanges();
            }
        }
    }
}
```

ここは、自前でDbContextのインスタンスを生成しています。

これで、初期データをMoviesテーブルに挿入するクラスができました。テーブルが空でないなら何も行いません。

## シード初期化コードの呼び出し

`Program.cs` で、次のように `Main` メソッドを変更します。

```c#
using Microsoft.Extensions.DependencyInjection;
using RazorPagesMovie.Models;

…

    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                SeedData.Initialize(services);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }
        host.Run();
    }
```

## 実行してみる        

では、動作するか確認してみます。

もし、データを登録してあったならば、`https://localhost:5001/Movies` ページに遷移し、Deleteリンクをクリックして、テーブル内のすべての行を削除してください。

それから、プログラムを停止し、再度アプリを起動します。これで、`SeedData.Initialize`メソッドが呼び出されて、初期データが挿入されるはずです。


![スクリーンショット 2019-11-04 11.12.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/7a746d47-ba72-24de-4074-bfc02f2b5925.png "スクリーンショット 2019-11-04 11.12.33.png")

想定通り、SeedDataで挿入した初期データが表示されました。


## appsettings.json

最後に、`appsettings.json`をのぞいてみます。

チュートリアルの`appsettings.json` を以下に示します。


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

```

`"ConnectionStrings":` が接続文字列を設定している個所です。

アプリ独自の設定も このファイルに書くことになるんでしょうね。ASP.NET MVC のweb.configの標準だと、

```html
<add key="myKey" value="myValue" />
```

などと、設定情報を書いていたわけですが、json形式なので、柔軟性がかなり高くなっていそうです。

どうやってアプリケーション独自の設定値を読み込むのかは、また後で調べようと思います。