【EF Core】コードファーストでPostgreSQLのDBを作成する【ASP.NET】

### はじめに
C#、ASP.NET初心者です。
コードファーストでPostgreSQLのDB構築を行う方法をまとめました。

コードファーストとは「項目を定義したクラスからDBを自動生成するEntity Frameworkの機能」です。
便利なのですが、C#の[命名規則](https://learn.microsoft.com/ja-jp/dotnet/csharp/fundamentals/coding-style/coding-conventions)（パスカルケース）に合わせてクラスを作成すると、そのままテーブル名・カラム名になります。
過去に [公式のMovieチュートリアル](https://learn.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/adding-model?view=aspnetcore-7.0&tabs=visual-studio) をもとに作成したのがこちらです。
![スクリーンショット 2023-02-25 152511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/f5165295-23c3-5a60-53bc-9dca65db23a9.png)
PostgreSQLでは、SQL発行時ダブルクォーテーションで囲っていないものは小文字に変換されるため、スネークケースで命名するのが望ましいです。
特にこだわりがなければSQL Serverを使用するのがいいのかもしれませんが、現場で使用しているPostgreSQLに慣れたい思いからいろいろ方法を調べ試行錯誤しました。

#### 前提
- Visual Studioがインストールされていること
- PostgreSQLがインストールされていること

### プロジェクトの作成
今までは.NET6.0でやっていたのですが、今回は.NET7.0でやってみます。
Model-View-Controller(MVC)を選択します。
![スクリーンショット 2023-02-25 141547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/f4578148-5246-5c46-9f52-fab5d2742617.png)
私のVisual Studioは.NET6.0しか選べなかったので.NET7.0をインストールしました。
（[.net7.0のインストール手順](https://mebee.info/2022/11/15/post-86021/) を参考にしました。）
![スクリーンショット 2023-02-25 144158.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/d7effaa2-b7e2-6d59-860f-e7bb94933449.png)

### NuGetパッケージの追加
[PostgreSQL で Movie チュートリアル (CORE)](http://surferonwww.info/BlogEngine/post/2022/07/18/use-posrgresql-for-movie-tutorial-instead-of-sql-server.aspx) を参考に以下をインストールしました。
SqlServerはスキャフォールディングで必要になる場合があるそうです。
![スクリーンショット 2023-02-25 190631.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/3c878a13-e88a-f9b3-348a-836774b3b070.png)
※.NETのバージョンアップをした場合、Visual Studioのアップデートを行っていないとエラーが出る場合があるのでご注意ください。

### モデルの作成
モデル（エンティティクラス）を作成します。
今回はEntity Framework Coreのチュートリアルである[こちら](https://learn.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/intro?view=aspnetcore-7.0)をもとに作成します。
![スクリーンショット 2023-02-25 173034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/a86e740a-b2d3-fad7-6b31-52d9d392083d.png)
テーブル名には [Table("テーブル名")]、カラム名には[Column("カラム名")] とスネークケースで書きます。
※チュートリアルと違うプロジェクト名で作成してしまったためご注意ください
```Student.cs
using System.ComponentModel.DataAnnotations.Schema;

namespace DotnetPractice.Models
{
    [Table("student")]
    public class Student
    {
        [Column("id")]
        public int ID { get; set; }

        [Column("last_name")]
        public string LastName { get; set; }

        [Column("first_mid_name")]
        public string FirstMidName { get; set; }

        [Column("enrollment_date")]
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}
```

```Enrollment.cs
using System.ComponentModel.DataAnnotations.Schema;

namespace DotnetPractice.Models
{
    
    public enum Grade
    {
        A, B, C, D, F
    }

    [Table("enrollment")]
    public class Enrollment
    {
        [Column("enrollment_id")]
        public int EnrollmentID { get; set; }

        [Column("course_id")]
        public int CourseID { get; set; }

        [Column("student_id")]
        public int StudentID { get; set; }

        [Column("grade")]
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}
```

```Course.cs
using System.ComponentModel.DataAnnotations.Schema;

namespace DotnetPractice.Models
{
    [Table("course")]
    public class Course
    {
        [Column("course_id")]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }

        [Column("title")]
        public string Title { get; set; }

        [Column("credits")]
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

```

### スキャフォールディング
チュートリアルと手順は違いますが、ここでスキャフォールディングを行います。
（[こちら](https://learn.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/adding-model?view=aspnetcore-7.0&%3Btabs=visual-studio&tabs=visual-studio)の手順を参考）
作成したモデルそれぞれについて、スキャフォールディングを行います。
Studentクラスで行った例が以下です。
①Controllersフォルダを右クリック→「追加」→「新規スキャフォールディングアイテム」を選択
②「Entity Framework を使用したビューがある MVC コントローラー」を選択
③モデルクラス：作成したいモデルを選択
　Dbcontext：デフォルトだとプロジェクト名＋Contextになるようです。任意の名前にしたい場合は、チュートリアルを参考に先に作成するとよさそうです。
　データベースプロバイダー：PostgreSQLはないですがSQL Serverで問題ないです。
![スクリーンショット 2023-02-25 194338.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/8fb3dbbe-cbac-7c70-d70d-c9d18c1a6aa7.png)
これでControllerやViewが自動生成されます。
残りのEnrollment、Courseクラスも同様に行います。

Dataフォルダに「プロジェクト名＋Context.cs」が自動生成されています。
```DotnetPracticeContext.cs
using Microsoft.EntityFrameworkCore;

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

        public DbSet<DotnetPractice.Models.Student> Student { get; set; } = default!;

        public DbSet<DotnetPractice.Models.Enrollment> Enrollment { get; set; } = default!;

        public DbSet<DotnetPractice.Models.Course> Course { get; set; } = default!;

    }
}
```

### PostgreSQLに合わせた修正
【Program.cs】
チュートリアルには「Startup.cs」とありますが、.NET6.0以降はありませんので「Program.cs」になります。
「var builder = WebApplication.CreateBuilder(args);」の下にコードが追加されています。
PostgreSQLの場合、「UseSqlServer」を「UseNpgsql」に書き換える必要があります。
```Program.cs
using Microsoft.EntityFrameworkCore;
using DotnetPractice.Data;

var builder = WebApplication.CreateBuilder(args);
// 以下を修正
builder.Services.AddDbContext<DotnetPracticeContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DotnetPracticeContext") ?? throw new InvalidOperationException("Connection string 'DotnetPracticeContext' not found.")));

// 以下略
```

【appsettings.json】
ConnectionStringsが追加されています。
以下がPostgreSQL用に書き換えたものです。「XXXXXXXX」に接続したいPostgreSQLのユーザ名とパスワードを入れてください。
Databaseは小文字で入力してください。ここに記載したものがそのままDB名になります。
```appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
　// 以下を修正
  "ConnectionStrings": {
    "DotnetPracticeContext": "Server=localhost;Port=5432;Database=practice;Username=XXXXXXXX;Password=XXXXXXXX"
  }
}
```

### Migrationの実行
ソリューションをリビルドしてエラーがないことを確認してから、Visual Studio のパッケージマネージャーコンソールで「Add-Migration InitialCreate」コマンドを実行します。
InitialCreateクラスがMigrationsフォルダに自動生成されます。
![スクリーンショット 2023-02-25 203103.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/450d46e5-3126-5dbe-9cd9-e0ebfbfb8529.png)
その後「Update-Database」コマンドを実行すると、DBが作成されます。
DB名・テーブル名・カラム名はスネークケースで作成できました。
![スクリーンショット 2023-02-25 203841.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3097017/ef9ac450-8ad8-b1d5-190c-4d5a20aaa94a.png)
しかしPKやFK名は大文字が含まれているので、Migrationで作成されたInitialCreateクラスを修正してから「Update-Database」コマンドを実行すれば反映されるんですかね。
（そこまでこだわってなかったので後で気づきました。。）

### 最後に
なかなか情報が見つけられなくて探すのに苦労しました。
Entity Framework Coreはいろいろなアノテーションがあってもっと勉強してみたいです。
SQL Serverも使ったことがないのでいずれ使ってみたいと思います。