Entity Framework に関する(自分のための)おさらい
ここ最近、Entity Framework Core の機能を試している
この記事は
コードファーストでつくったDBを、再度リバースエンジニアリングしてコードに戻すことを試す
そのとき、もとのコードと、リバースエンジニアリングしてきたコードのあいだに、どんな違いがあるのか?
気になったので試して違いを感じる
リバースエンジニアリング先のプロジェクトをつくる
前回は、IntroプロジェクトからコードファーストでDBをつくった
今回は、そのDBをリバースエンジニアリングすることで、モデルとコンテキストをIntroReverseプロジェクトにスキャフォールディングする(用語の使い方が合っているかわからない)
プロジェクトの準備
-
IntroReverseプロジェクトをつくる - スタートアッププロジェクトの参照に
IntroReverseプロジェクトを追加する - EF Core関連の必要なパッケージをインストールする
# ライブラリプロジェクトを新規作成する
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 からモデルとコンテキストを生成できる
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ファイルが自動生成された
モデルを比較する
公式ドキュメントのモデルを参考につくったIntroと
先ほどリバースエンジニアリングしてつくったIntroReverseを比較する
-
Blogクラス -
Postクラス -
BloggingContext/BloggingReverseContextクラス
Blogクラス
- クラスに
partialが付加された - DBのフィールドにならない
Post型のコレクションが、ListからICollectionに変更された - コンストラクタが新たに追加され、コレクションのインスタンスに
HashSetが採用された - コレクションには
virtual修飾子がついた
- 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修飾子がついた
- 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が追加された
- 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(今ここ)
Add-Migration -Name InitReverse -Context BloggingReverseContext -Project IntroReverse -StartupProject WPF_EFCore
Update-Database -Project IntroReverse -Context BloggingReverseContext
Visual Stduio で DB 扱うのに必要そうなパッケージを入れる
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サーバーの追加
localhostのDockerコンテナ PORT 11433 に、BloggingとBloggingReverse(さっきつくった)がある
デザイナーの表示T-SQLの比較
コードファーストのBloggingDBと、コードファースト→リバースエンジニアリング→コードファーストのBloggingReverseDBは、完全に一致しました
-- 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)
);
-- 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にしたらどっちも同じだったということが分かった


