ASP.NET MVCにデータベースを接続する

## 前回の記事

【ViewModel】ASP.NET MVCでサーバー側と画面側でデータを受け渡す

https://qiita.com/shironana/items/06ec2cb2e17fb064a857

## 本記事のゴール
- 既存プロジェクトにデータベースを接続する
- マイグレーションにてデータベース内にテーブルを作成する

本記事の全体像は以下画像のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/08ace84a-45a2-8576-acdb-87cc77033832.png)

## はじめに（複数プロジェクト構成について）

今回以降はシステムの役割を増やしていくためシステムが複雑化していきます。これに伴ってこれまでの単一プロジェクト構成から複数プロジェクト構成へ変更してシステム開発を行います。

同じ環境で進めたい方、複数プロジェクト構成について知りたい方は以下の記事「ASP.NETで複数プロジェクト構成を作成する」を参照してください。

https://qiita.com/shironana/items/6697750d380af58ac91b

:::note warn
単一プロジェクト（DBとアプリケーションを同じプロジェクトに置く）でも本記事の手順で接続が可能です。その際はプロジェクト名を適宜読み替えてください。
:::

## データベースを用意する

まずは接続対象のデータベースを用意します。

なお、本記事中では「SQL Server LocalDB」ではなく、外部DBに対応した接続方法で接続を行います。
SQL Server LocalDBの詳細は以下Microsoft公式ドキュメントを参照してください。

https://learn.microsoft.com/ja-jp/sql/database-engine/configure-windows/sql-server-express-localdb?view=sql-server-ver16

データベースの種類についての制限はありませんが、**記事内の見本では自分のPC内（ローカル環境）に「SQL Server」の無料版を用意**して行います。

SQL Serverを用意する方法については以下記事「SQL Server Express（無料版）を導入する」を参考にしてください。

https://qiita.com/shironana/items/c98e38bc85c6f5a9bd3c


## データベース用プロジェクトを追加する

データベースとの接続は、開発するWebアプリケーションとは別のプロジェクト内で管理するため、既存のWebプロジェクトとは別のプロジェクトを作成します。

1．「ソリューション」を右クリック→「追加」→「新しいプロジェクト」をクリック

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/5cfd8a9c-4b53-fb3d-2385-6f07d56bacb4.png)

2．「クラスライブラリ」を選択して作成、今回は「Database」というプロジェクト名を付けました

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/5e94787b-a8c5-ca97-aea8-9aa4988b0135.png)
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/bd0f4193-3b0b-ab84-a039-e6f57a275659.png)

## 依存関係の設定

プロジェクト「Web」内でプロジェクト「Database」内のクラスを参照する必要があるため、ビルドの順序を設定します。

1．「ソリューション」を右クリック→「プロジェクトの依存関係」をクリック

![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/186ce1c0-1872-fbf1-4e03-5b2ec9880e3c.png)

2．プロジェクト「Web」の依存先の「Database」にチェックをつけて「OK」をクリック

![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/f03cccd1-db36-33b7-af94-6a5ad0704fa8.png)


3．再度開き、ビルドの順序で「Database」が上に記載されていれば完了

![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/0b15673c-f51e-6b83-9a74-a41d13c1ee4e.png)

## プロジェクト参照の設定

プロジェクト「Web」内でプロジェクト「Database」内のクラスを参照する必要があるため、プロジェクト参照を設定します。

1．プロジェクト「Web」内の「依存関係」を右クリック→「プロジェクト参照の追加」をクリックし、参照マネージャーを開く

![image24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/1b3bcfae-09b1-31c4-e6ae-9382e56179df.png)

2．「Database」を選択して「OK」をクリック

![image25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/7b675fd4-67ef-362c-d143-36d87a834cfd.png)

## Entity Framework Core本体のインストール

1．上部バーの「ツール」→「NuGetパッケージマネージャー」→「ソリューションのNuGetパッケージの管理」をクリック

![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/dfcd2300-040d-208d-78d4-1a0e52a7941c.png)

2．「Microsoft.EntityFrameworkCore」と検索し、**適切なパッケージ**を選択してインストール

インストール先のプロジェクトは、DBを扱うプロジェクトを選択してください。今回は「Database」にチェックをつけます。

:::note warn
適切なパッケージについて、
SQLServerの場合は `Microsoft.EntityFrameworkCore.SqlServer`
SQLiteの場合は `Microsoft.EntityFrameworkCore.Sqlite`
その他の場合は以下Microsoft公式ドキュメント「データベース プロバイダー」を参照してください。
https://learn.microsoft.com/ja-jp/ef/core/providers/?tabs=dotnet-core-cli
:::

![image8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/d6d14d8e-bf0f-1018-44ba-87064e1eccdb.png)

## Entity Framework Core Toolsのインストール

`Entity Framework Core Tools` は、DBに対してのマイグレーション等を行う際に必要なパッケージです。

1．「Microsoft.EntityFrameworkCore.Tools」を選択してインストール

インストール先のプロジェクトは、実際にDBを読み込んで使用するプロジェクト（厳密には接続文字列を読み込むプロジェクト）を選択してください。今回は「Web」にチェックをつけます。

![image9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/b044bf15-16fe-b606-f97f-cb7861967db2.png)


## DBエンティティモデルの作成

ここではエンティティ（実際にDBに置くテーブル設計）を管理するモデルを作成します。

今回は以下のような簡易なエンティティを作成します。

![image10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/61d6ba28-5e08-846e-5adb-15344d17e108.png)


1．プロジェクト「Database」を右クリック→「追加」→「新しいフォルダ」でフォルダを作成し、「Entities」などの分かりやすい名前に変更する。

![image11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/110d0977-c7d1-5445-c38d-009e952588a0.png)

2．1で追加したファイルを右クリック→「追加」→「クラス」で新しい項目の追加ダイアログを表示する。

![image12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/13031779-9e42-5201-1002-826449539ccb.png)

3．クラス名を、"追加するテーブル名"と同一に設定する

![image13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/47e32e97-8b26-e5a3-fd9a-0cdb6b3e6dbd.png)

4．以下の変更を行い、エンティティの定義を行います。
- 不要なusingの削除
- クラスのアクセス修飾子を「internal」から「public」へ変更
- プロパティの定義
    - 主キー項目には `[Key]` 属性を付与
        - 連番数字を自動生成しない場合は `[DatabaseGenerated(DatabaseGeneratedOption.None)]` 属性を付与
    - 必須項目はNull非許容型に、**任意項目はNull許容型**にする
    - 文字列型に最大値 `[MaxLength(最大文字数)]` を設定する

```Gourmet.cs
using System.ComponentModel.DataAnnotations;

namespace Database.Entities
{
    public class Gourmet
    {
        /// <summary>
        /// グルメID
        /// </summary>
        [Key]
        public int GourmetId { get; set; }

        /// <summary>
        /// グルメ名
        /// </summary>
        [MaxLength(100)]
        public required string GoumetName { get; set; }

        /// <summary>
        /// 県コード
        /// </summary>
        public int PrefectureCode { get; set; }

        /// <summary>
        /// 評価
        /// </summary>
        public int? Rate { get; set;}
    }
}
```
```Prefecture.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Database.Entities
{
    public class Prefecture
    {
        /// <summary>
        /// 県コード
        /// </summary>
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int PrefectureCode { get; set; }

        /// <summary>
        /// 県名
        /// </summary>
        [MaxLength(100)]
        public required string PrefectureName { get; set; }
    }
}
```

※定義方法の詳細は以下Microsoft公式ドキュメントを参照してください。
https://learn.microsoft.com/ja-jp/ef/core/modeling/entity-types?tabs=data-annotations
https://learn.microsoft.com/ja-jp/ef/core/modeling/keys?tabs=data-annotations

:::note warn
`required` はC# 11 以降に追加されたもので、null非許容型を維持する一つの方法です。詳細は以下Microsoft公式ドキュメント「Null 許容参照型の使用」を参照してください。
https://learn.microsoft.com/ja-jp/ef/core/miscellaneous/nullable-reference-types#non-nullable-properties-and-initialization
:::

:::note warn
今回はリレーションの設定については考慮に入れていません。詳細は以下Microsoft公式ドキュメント「リレーションシップの概要」を参照してください。
https://learn.microsoft.com/ja-jp/ef/core/modeling/relationships
:::

## 接続文字列の設定

DBへの接続文字列はソースコードに直接設定することを防ぎ、さらに環境ごとに管理を行えるようにするため `appsettings.json` などに指定することが推奨されています。

1．Webプロジェクト配下の `appsettings.json` に接続文字列を設定します。Keyは `ConnectionStrings` とし、DBごとに設定します。接続文字列はDBの種類により形式および接続方法が異なります。
DBごとの接続文字列のKeyは「～Context」とする慣例があり、後ほど作成するコンテキストクラスの名前と一致することが推奨されます。今回は「GourmetDbContext.cs」と設定します。

```diff_json:appsettings.json
  {
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft.AspNetCore": "Warning"
      }
    },
-   "AllowedHosts": "*"
+   "AllowedHosts": "*",
+   "ConnectionStrings": {
+       "GourmetDbContext": "Data Source=DESKTOP-*****\\SHIRONANASERVER;Initial Catalog=GourmetDB;User ID=*****;Password=*****;Trust Server Certificate=True"
+   }
  }
```

2．Webプロジェクト配下の `Program.cs` に `appsettings.json` から情報を取得できるようコードを追加します。

```diff_c_sharp:Program.cs
  var builder = WebApplication.CreateBuilder(args);

+ // 設定ファイルから設定値を取得
+ var config = new ConfigurationBuilder()
+     .AddJsonFile("appsettings.json")
+     .Build();
+
  // Add services to the container.
  builder.Services.AddControllersWithViews();

  var app = builder.Build();

  (... 省略 ...)
```

## DBコンテキストの設定

DBコンテキストを作成し、先ほど作成したエンティティモデルとデータベース間の対応関係を定義します。

1．Databaseプロジェクト配下 `Class1.cs` の名前を変更、先ほど `appsettings.json` に設定した名前と同じクラス名にします。

![image14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/5e174b63-b693-522c-e3e0-d0c3bacea9fa.png)

以下のような警告は既存コードの参照を自動で整えてくれるものなので、「はい」を選択します。

![image15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/65f513e5-d47b-6b5f-85e8-1f6bf13474c7.png)

2．以下の変更を行い、DBコネクションの定義を行います。
- 依存性の注入用コンストラクタの作成
- 作成したエンティティの定義
- エンティティの追加設定（必要な場合のみ）

```GourmetDbContext.cs
using Database.Entities;
using Microsoft.EntityFrameworkCore;

namespace Database
{
    public class GourmetDbContext : DbContext
    {
        // 依存性の注入用コンストラクタ
        public GourmetDbContext(DbContextOptions<GourmetDbContext> options) : base(options) { }

        // エンティティ定義（作成したエンティティと名前が揃うように設定）
        public DbSet<Gourmet> Gourmet { get; set; }
        public DbSet<Prefecture> Prefecture { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            /* エンティティの追加設定など */
        }
    }
}
```

## 依存性の注入（DB接続文字列の適用）

1．`Program.cs` でDBコンテキストクラスの「依存性の注入」を行い、マイグレーションに使用する接続文字列を設定します。

```diff_c_sharp:Program.cs
  var builder = WebApplication.CreateBuilder(args);

  // 設定ファイルから設定値を取得
  var config = new ConfigurationBuilder()
      .AddJsonFile("appsettings.json")
      .Build();
 
  // Add services to the container.
  builder.Services.AddControllersWithViews();
+ builder.Services.AddDbContext<GourmetDbContext>(options => {
+     options.UseSqlServer(config.GetConnectionString(nameof(GourmetDbContext)));
+ });

  var app = builder.Build();

  (... 省略 ...)
```

## マイグレーションによりDBに反映させる

1．上部ツールバーの「表示」→「その他のウィンドウ」→「パッケージマネージャーコンソール」からパッケージマネージャーコンソールを開く

![image16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/0a3b3307-485f-e8a4-4ef6-4def9dfd0490.jpeg)

2．既定のプロジェクトを「Database」（DBコンテキストが存在するプロジェクト）に変更する

![image17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/d29288f6-93d7-f961-2678-2365a0c7d1a9.png)


3．ソリューションを右クリック→「スタートアッププロジェクトの構成」でスタートアッププロジェクト構成画面を開く

![image18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/06d9f374-1d16-af04-094c-49bf90356dd8.png)

4．シングルスタートアッププロジェクトが「Web」（実際にDBを読み込んで使用するプロジェクト）になっていることを確認

![image19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/b37aa228-65af-2b43-2ad1-c7de95343313.png)

5．以下コマンドを入力してマイグレーションを実行

```
Add-Migration {任意の名前}
```

![image20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/37a93666-825c-519c-37a1-088ed60f86fc.png)


6．Migrationsフォルダにマイグレーションファイルが生成されたことを確認

![image21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/bc28f526-40da-54da-65dd-24badfd71420.png)

:::note
直前のマイグレーション操作は以下コマンドで取り消すことができます。
```
Remove-Migration
```
:::

7．以下コマンドを入力してマイグレーションファイルの内容をDBに反映させる。

```
Update-Database
```
![image22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/f1e077ee-a9c5-f74c-b438-0153683cd118.png)


8．DBに変更内容が反映されていることを確認する

![image23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/b7eaca04-5f89-a74a-9d80-7f84b20e72f8.png)


:::note
テーブル設計を変更する場合（制限文字数変更、カラム追加など）は、DBエンティティモデルを修正した後、 1～8 のマイグレーション手順を再度実行するとデータベースに反映されます。
:::

## おまけ：マイグレーションやDB関連設定をDatabaseプロジェクトだけで完結させる

本記事では接続文字列をWebプロジェクト側に設定してマイグレーションを行っていますが、Web系プロジェクトが増えると、プロジェクトごとに設定が必要になります。プロジェクトごとにDBが変わる場合は問題ないですが、同一DBに接続する場合は管理が煩雑になってしまいます。
そこで、DB関連設定をDatabaseプロジェクトだけで完結させることができないか試行錯誤した結果を以下記事にまとめましたのでぜひご覧ください。

https://qiita.com/shironana/items/2f3be12f343aad321272

## 参考記事

EF Core の概要 - Microsoft Learn
https://learn.microsoft.com/ja-jp/ef/core/get-started/overview/first-app?tabs=visual-studio

エンティティのプロパティ - Microsoft Learn
https://learn.microsoft.com/ja-jp/ef/core/modeling/entity-properties?tabs=data-annotations%2Cwithout-nrt

キー - Microsoft Learn
https://learn.microsoft.com/ja-jp/ef/core/modeling/keys?tabs=data-annotations

## 次回の記事

ASP.NET MVCでデータベースを操作する

https://qiita.com/shironana/items/9125feba6454901d5465