『Blazor 入門』のサンプル DB を Docker で構築する

[書籍リンク]: https://www.nikkeibp.co.jp/atclpubmkt/book/20/P96920/
[ベースイメージ]: https://hub.docker.com/_/microsoft-mssql-server

[登録されたレコード]:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509844/1651f2ba-e885-750d-0b98-718661c8cc84.png
[実行結果]:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509844/ac5a9b29-d399-772c-619d-44c8404d3511.png

Blazor を扱った技術書として、増田智明さんの著書『Blazor 入門』があります。

- [Blazor入門　ブラウザで動作するC#を使ったシングルページアプリケーション開発｜日経の本 日経BP][書籍リンク]

本書では第 9 章から Entity Framework を用いたデータベースアクセスについて解説されますが、その導入として SQL Server データベースをローカル環境上に構築しています。

近年では Linux 環境で SQL Server が利用できるようになったので、Docker で上記の解説に必要な DB コンテナーを構築してみたいと思います。

## 前提確認

以下の方針に則り、コンテナーを構成します。

- [SQL Server for Linux の Docker イメージ][ベースイメージ]を使って上記の用途をカバーする
- 本書の作業フローに極力影響を与えない

また、自分の開発環境の構成は以下の通りです。

```terminal
PS > dotnet --list-sdks
5.0.401 [C:\Program Files\dotnet\sdk]

PS > docker --version
Docker version 20.10.10, build b485636
```

本書における開発環境は .NET Core 3.1 が想定されていますが、.NET 5 でも大きな違いはないと思われます。

## コンテナー定義の作成

以下の `docker-compose.yml` ファイルを作成します。

```yml
version: '3.4'

services:
  mssql:
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: P@ssw0rd!
    ports:
      - 1433:1433
    volumes:
      - "blazor-introduction-mssql:/var/opt/mssql"
      - "./mssqlserver/ddl:/usr/src/ddl:ro"

volumes:
  blazor-introduction-mssql:
```

コンテナーを起動する際は、以下のコマンドを実行します。


```terminal
docker compose up mssql
```

### コンテナー定義の解説

`docker-compose.yml` の記載内容について、以下で解説します。

#### ベースイメージ

```yml
mssql:
  image: mcr.microsoft.com/mssql/server:2019-latest
```

SQL Server 2019 のベースイメージ `mcr.microsoft.com/mssql/server:2019-latest` を参照し、`mssql` コンテナーとして定義しています。

#### 環境変数

```yml
environment:
  ACCEPT_EULA: Y
  SA_PASSWORD: P@ssw0rd!
```

`environment` の節に指定した環境変数は、それぞれ以下の意味を持ちます。

変数名 | 意味
-|-
`ACCEPT_EULA` | ライセンス条項への同意
`SA_PASSWORD` | 管理者アカウント (System Administrator) 用のログイン パスワード

パスワードは DB へのログインの際に使用します。

#### 使用ポートの指定

```yml
ports:
  - 1433:1433
```

SQL Server が標準で使用するポート `1433` を、ホスト側のポートと対応させています。ホスト側で既にこのポートを使用している場合は、別のポートを指定する必要があります。

#### データベースの状態の保存

```yml
services:
  mssql:
    # ... 省略
    volumes:
      - "blazor-introduction-mssql:/var/opt/mssql"
      # ... 省略

volumes:
  blazor-introduction-mssql:
```

コンテナーを終了しても DB の状態が維持されるよう、`/var/opt/mssql` 以下のパスを、Docker ボリューム `blazor-introduction-mssql` 上で永続化する構成としています。

#### DDL ファイルの共有

```yml
volumes:
  # ... 省略
  - "./mssqlserver/ddl:/usr/src/ddl:ro"
```

コンテナー内のパス `/usr/src/ddl` へ、テーブルを作成するための DDL ファイルをマウントしています。DDL ファイルはコンテナー側から変更を加えることはないため、`:ro` フラグにより読み取り専用 (Read-Only) として宣言しています。

ホスト側で対応するパスは、ここでは相対参照により `./mssqlserver/ddl` として指定しました。ホスト側でこのパスに保存したファイルが、コンテナーからは `/usr/src/ddl` 以下に存在するものとして扱われます。

## データベースの構成

### データベースの作成

コンテナー起動後は、データベース `blazordb` を作成する DDL を実行します。

以下の SQL を記述したファイルを `database.sql` として、`./mssqlserver/ddl` へ保存します。

```sql
EXECUTE AS LOGIN = 'sa'
GO

CREATE DATABASE blazordb
GO
```

これは管理者アカウント `sa` でログインし、解説に用いるデータベース `blazordb` を作成するクエリです。

`docker compose exec` コマンドにより、コンテナー内で目的の DDL ファイルを実行します。

```sh
docker compose exec mssql /opt/mssql-tools/bin/sqlcmd -U SA -P P@ssw0rd! -i /usr/src/ddl/database.sql
```

### テーブルの作成

続いて、データベース `blazordb` 上にテーブル `books` を作成します。

この時に実行する DDL ファイルは以下の内容になります。管理者アカウント `sa` により、先ほど作成したデータベース `blazordb` へ接続し、テーブル `books` を作成するクエリです。

```sql
EXECUTE AS LOGIN = 'sa'
GO

USE blazordb
GO

CREATE TABLE [dbo].[books]
(
   [id] INT IDENTITY(1, 1) NOT NULL
   , [title] NVARCHAR(50) NOT NULL
   , [author] NVARCHAR(50) NOT NULL
   , [price] INT NOT NULL
   , [publisher] NVARCHAR(50) NULL
   , CONSTRAINT [PK_books] PRIMARY KEY CLUSTERED
   (
      [id] ASC
   ) WITH (
      PAD_INDEX = OFF
      , STATISTICS_NORECOMPUTE = OFF
      , IGNORE_DUP_KEY = OFF
      , ALLOW_ROW_LOCKS = ON
      , ALLOW_PAGE_LOCKS = ON
   )
) ON [PRIMARY]
GO
```

これを `books.sql` として保存し、コンテナー内で実行します。

```sh
docker compose exec mssql /opt/mssql-tools/bin/sqlcmd -U SA -P P@ssw0rd! -i /usr/src/ddl/books.sql
```

### レコードの登録

レコードを登録する場合は、コンテナー側の CLI か、Visual Studio 上の SQL Server オブジェクト エクスプローラーを利用します。

CLI を利用する場合は、以下のコマンドによりサーバーへ接続します。

```sh
docker compose exec mssql /opt/mssql-tools/bin/sqlcmd -U SA -P P@ssw0rd!
```

以下の INSERT 文を実行し、レコードを登録します。

```sql
INSERT INTO [dbo].[books]
   ([id], [title], [author], [price], [publisher])
VALUES (1, N'タイトルA', N'著者A', 5000, N'出版社A')
GO
```

最終的に、以下のような `books` レコードが登録された状態とします。

![登録されたレコード]

## データベースへの接続

サーバーは Linux コンテナー上で稼働しており、Windows 認証は使用できません。そのため、接続文字列を SQL Server 認証 (ユーザー名とパスワードによる認証) へ変更します。

具体的には `Models\blazordbContext.cs` へ記載する接続文字列を、以下の内容へ変更します。

```diff
  if (!optionsBuilder.IsConfigured)
  {
-     optionsBuilder.UseSqlServer("Server=.;Database=blazordb;Trusted_connection=True");
+     optionsBuilder.UseSqlServer("Server=.;Database=blazordb;User ID=sa;Password=P@ssw0rd!");
  }
```

変更前の文字列に含まれる `Trusted_connection=True` が、DB への接続に Windows 認証を使用することを表しています。これを `User ID` と `Password` の 2 項目へ置き換えることで、SQL Server 認証により接続する構成となります。

SQL Server 認証を使用するのは、Entity Framework を用いたスキャフォールディング操作を行う場合も同様です。コマンドを実行する際の認証情報に、上記と同様のパラメータを指定します。

```sh
dotnet ef dbcontext scaffold "Server=.;Database=blazordb;User ID=sa;Password=P@ssw0rd!" Microsoft.EntityFrameworkCore.SqlServer -o Models
```

## 実行例

前述の通り Docker コンテナーを使用する場合は、接続文字列の指定に修正を加えるだけで対応が可能です。Blazor アプリケーションを実行した結果として、以下の内容が返されます。

![実行結果]

SQL Server をホスト上にインストールしない場合でも、Docker を利用して対応可能であることが確認できたと思います。