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の比較
コードファーストのBlogging
DBと、コードファースト→リバースエンジニアリング→コードファーストのBloggingReverse
DBは、完全に一致しました
-- 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にしたらどっちも同じだったということが分かった