【Entity Framework Core】コードファーストで移行したDBをリバースエンジニアリングして違いを感じる

# Entity Framework に関する（自分のための）おさらい

ここ最近、Entity Framework Core の機能を試している

- [この記事](https://qiita.com/t13801206/items/4c2e85fc2732c4de5533)では**データベースファースト** でモデルの生成をした
- [この記事](https://qiita.com/t13801206/items/9330dd3a540966261320)では**コードファースト**で DB を生成した

# この記事は

コードファーストでつくったDBを、再度リバースエンジニアリングしてコードに戻すことを試す

そのとき、**もとのコードと、リバースエンジニアリングしてきたコードのあいだに、どんな違いがあるのか？**
気になったので試して違いを感じる

https://docs.microsoft.com/ja-jp/ef/core/managing-schemas/scaffolding

# リバースエンジニアリング先のプロジェクトをつくる

[前回](https://qiita.com/t13801206/items/9330dd3a540966261320)は、`Intro`プロジェクトからコードファーストでDBをつくった
今回は、そのDBをリバースエンジニアリングすることで、モデルとコンテキストを`IntroReverse`プロジェクトにスキャフォールディングする（用語の使い方が合っているかわからない）

## プロジェクトの準備

1. `IntroReverse`プロジェクトをつくる
1. スタートアッププロジェクトの参照に`IntroReverse`プロジェクトを追加する
1. EF Core関連の必要なパッケージをインストールする

```Powershell:パッケージマネージャーコンソール
# ライブラリプロジェクトを新規作成する
dotnet new classlib --framework "netcoreapp3.1" -o IntroReverse

# ライブラリプロジェクトをスタートアッププロジェクトの参照に追加する
dotnet sln add .\IntroReverse\IntroReverse.csproj
dotnet add .\WPF_EFCore\WPF_EFCore.csproj reference .\IntroReverse\IntroReverse.csproj

# ライブラリプロジェクトに必要なパッケージをインストールする
dotnet add .\IntroReverse\IntroReverse.csproj package Microsoft.EntityFrameworkCore
dotnet add .\IntroReverse\IntroReverse.csproj package Microsoft.EntityFrameworkCore.SqlServer
```

## DBからスキャフォールディングする

たった 1 行のコマンドで、 DB からモデルとコンテキストを生成できる

```Powershell:パッケージマネージャーコンソール
Scaffold-DbContext  'Data Source="localhost, 11433";Initial Catalog=Blogging;User ID=sa;Password=SqlPass1234' Microsoft.EntityFrameworkCore.SqlServer  -Project IntroReverse -OutputDir Models -ContextDir Context -Context BloggingReverseContext
```

横スクロールが長いので、主要なパラメータだけ抜粋して表にする

|項目|値|説明|
|-|-|-|
|Initial Catalog=|Blogging|接続先のデータベースの名前
|-Project|IntroReverse|データベースをもとにモデルとコンテキストを作成する先のプロジェクトの名前
|-OutputDir|Models|フォルダの名前。テーブルのエンティティがここに生成される
|-ContextDir|Context|フォルダの名前。コンテキストのクラスがここに生成される
|-Context|BloggingReverseContext|コンテキストのクラス名

### スキャフォールディングした結果

2つのフォルダと、3つの`.cs`ファイルが自動生成された

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/ade88abb-bc5e-148a-5f1d-e4ceb51d6a0b.png)

# モデルを比較する

[公式ドキュメントのモデル](https://docs.microsoft.com/ja-jp/ef/core/#the-model)を参考につくった`Intro`と
先ほどリバースエンジニアリングしてつくった`IntroReverse`を比較する

- `Blog`クラス
- `Post`クラス
- `BloggingContext`/`BloggingReverseContext`クラス

## `Blog`クラス

- クラスに`partial`が付加された
- DBのフィールドにならない`Post`型のコレクションが、`List`から`ICollection`に変更された
- コンストラクタが新たに追加され、コレクションのインスタンスに`HashSet`が採用された
- コレクションには`virtual`修飾子がついた

```diff_c_sharp:Blogクラス
- public class Blog
+ public partial class Blog
  {
+     public Blog()
+     {
+         Posts = new HashSet<Post>();
+     }
      public int BlogId { get; set; }
      public string Url { get; set; }
      public int Rating { get; set; }
-     public List<Post> Posts { get; set; }
+     public virtual ICollection<Post> Posts { get; set; }
  }
```


## `Post`クラス

- クラスに`partial`が付加された
- DBのフィールドにならない`Blog`型には`virtual`修飾子がついた

```diff_c_sharp:Postクラス
- public class Post
+ public partial class Post
  {
      public int PostId { get; set; }
      public string Title { get; set; }
      public string Content { get; set; }
      public int BlogId { get; set; }
-     public Blog Blog { get; set; }
+     public virtual Blog Blog { get; set; }
  }
```

## `BloggingContext`/`BloggingReverseContext`クラス

コンテキストは追加箇所が多い

- クラスに`partial`が付加された
- 2 つのコンストラクタが追加された
- `DbSet<T>`に`virtual`修飾子が追加された
- `optionsBuilder`のメソッドを呼ぶ前に、`IsConfigured`を確認している
- `OnModelCreating`メソッドでいろいろしている
- `partial`な`OnModelCreatingPartial`が追加された

```diff_c_sharp:BloggingContextクラス/BloggingReverseContextクラス
- public class BloggingContext : DbContext
+ public partial class BloggingReverseContext : DbContext
  {
+     public BloggingReverseContext() { }
+     public BloggingReverseContext(DbContextOptions<BloggingReverseContext> options) : base(options) { }

-     public DbSet<Blog> Blogs { get; set; }
-     public DbSet<Post> Posts { get; set; }
+     public virtual DbSet<Blog> Blogs { get; set; }
+     public virtual DbSet<Post> Posts { get; set; }
  
      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      {
-         optionsBuilder.UseSqlServer("Data Source=\"localhost, 11433\";Initial Catalog=Blogging;User ID=sa;Password=SqlPass1234");
+         if (!optionsBuilder.IsConfigured)
+         {
+             optionsBuilder.UseSqlServer("Data Source=\"localhost, 11433\";Initial Catalog=Blogging;User ID=sa;Password=SqlPass1234");
+         }
      }
  
+     protected override void OnModelCreating(ModelBuilder modelBuilder)
+     {
+         modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");
+ 
+         modelBuilder.Entity<Post>(entity =>
+         {
+             entity.HasIndex(e => e.BlogId, "IX_Posts_BlogId");
+ 
+             entity.HasOne(d => d.Blog)
+                 .WithMany(p => p.Posts)
+                 .HasForeignKey(d => d.BlogId);
+         });
+ 
+         OnModelCreatingPartial(modelBuilder);
+     }
  
+     partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
  }
```

# もういっかいDBに戻してみる

`BloggingReverseContext`の接続文字列を`Initial Catalog=BloggingReverse;`にして、DBをつくる

コード → DB → コード（さっきみた） → **DB(今ここ）**

```Powershell:パッケージマネージャーコンソール
Add-Migration -Name InitReverse -Context BloggingReverseContext -Project IntroReverse -StartupProject WPF_EFCore
Update-Database -Project IntroReverse -Context BloggingReverseContext
```

Visual Stduio で DB 扱うのに必要そうなパッケージを入れる

```Powershell:Powershell
dotnet add .\WPF_EFCore\WPF_EFCore.csproj  package Microsoft.EntityFrameworkCore.Tools
dotnet add .\WPF_EFCore\WPF_EFCore.csproj  package Microsoft.EntityFrameworkCore.Design
# ライブラリプロジェクトにも入れておく
```

Visual Stduio ツール → SQLサーバーの追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/cc7a6edf-bd4a-61ed-2aa2-56e91f1021bc.png)

localhostのDockerコンテナ PORT 11433 に、`Blogging`と`BloggingReverse`（さっきつくった）がある

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/f23bbc5a-1f82-2c18-c626-9c16d3135efd.png)

## デザイナーの表示T-SQLの比較

コードファーストの`Blogging`DBと、コードファースト→リバースエンジニアリング→コードファーストの`BloggingReverse`DBは、**完全に一致しました**

```sql:Blog
-- Blogging
CREATE TABLE [dbo].[Blogs] (
    [BlogId] INT            IDENTITY (1, 1) NOT NULL,
    [Url]    NVARCHAR (MAX) NULL,
    [Rating] INT            NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
);

-- BloggingReverse
CREATE TABLE [dbo].[Blogs] (
    [BlogId] INT            IDENTITY (1, 1) NOT NULL,
    [Url]    NVARCHAR (MAX) NULL,
    [Rating] INT            NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
);
```

```sql:Posts
-- Blogging
CREATE TABLE [dbo].[Posts] (
    [PostId]  INT            IDENTITY (1, 1) NOT NULL,
    [Title]   NVARCHAR (MAX) NULL,
    [Content] NVARCHAR (MAX) NULL,
    [BlogId]  INT            NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs] ([BlogId]) ON DELETE CASCADE
);


GO
CREATE NONCLUSTERED INDEX [IX_Posts_BlogId]
    ON [dbo].[Posts]([BlogId] ASC);

-- BloggingReverse
CREATE TABLE [dbo].[Posts] (
    [PostId]  INT            IDENTITY (1, 1) NOT NULL,
    [Title]   NVARCHAR (MAX) NULL,
    [Content] NVARCHAR (MAX) NULL,
    [BlogId]  INT            NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs] ([BlogId]) ON DELETE CASCADE
);


GO
CREATE NONCLUSTERED INDEX [IX_Posts_BlogId]
    ON [dbo].[Posts]([BlogId] ASC);
```

# おわりに

コードファーストからのリバースエンジニアリングで逆輸入したコードには
元のコードにいろいろ`partial`やら`virtual`やらコンストラクタが加わっていたけれど
DBにしたらどっちも同じだったということが分かった