はじめに
この記事では、Blogging
データベースをつくった
今回は、Blogging
データベースにシードデータを与える
データシード処理とは
データシード処理とは、データベースに初期データセットを設定するプロセスのことです。
データベースをつくったタイミングで、そのデータベースにサンプルデータを挿入できるというもの
特にローカルでいろいろ試しているうちは、検証用にデータが必要になることが多いので
あらかじめ任意の初期データセットをセットできると便利でよい
DbContext
を継承したクラスを編集する
ここから実際に手を動かす
シードデータを定義するために
DbContext
を継承したBloggingContext
を編集する
具体的には、基底クラスに用意されているOnModelCreating
メソッドをオーバーライドして処理を置き換える
今回は、Blog
クラスには2つのレコード、Post
クラスには4つのレコードを、それぞれ初期値として設定する
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// BlogSeed
modelBuilder.Entity<Blog>().HasData(
new Blog { BlogId = 1, Url = "http://one.com" },
new Blog { BlogId = 2, Url = "http://two.com" }
);
// PostSeed
modelBuilder.Entity<Post>().HasData(
new Post { BlogId = 1, PostId = 1, Title = "title11", Content = "content11" },
new Post { BlogId = 1, PostId = 2, Title = "title12", Content = "content12" },
new Post { BlogId = 2, PostId = 3, Title = "title23", Content = "content23" },
new Post { BlogId = 2, PostId = 4, Title = "title24", Content = "content24" }
);
}
データベースを移行する
次の手順で、データシード処理をデータベースに反映させる
- 移行を追加する
Add-Migration
- 移行をデータベースに反映させる
Update-Database
移行を追加するAdd-Migration
コンテキストを編集したので、編集した内容を記録する
(=Add-Migration
で、移行の履歴に追加する)
Add-Migration -Name Seed -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
使っているコードは過去記事からの続いているので
このAdd-Migraiton
は、Init
マイグレーションにつづいて、2回目のマイグレーションとなる
移行の履歴を確認するGet-Migration
Get-Migration
コマンドにより、BloggingContext
に関する移行の履歴を確認できる
また、それらの移行がデータベースに反映されているか否かも確認できる
Get-Migration -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
# id name safeName applied
# -- ---- -------- -------
# 202108********_Init Init Init True
# 202109********_Seed Seed Seed False
Init
につづいてSeed
が追加されていることがわかる
この時点ではまだデータベースには変更が反映されていないので、applied
はfalse
である
ビジュアルで確認すると、プロジェクトのMigrationsフォルダは以下のようになっている
移行をデータベースに反映させるUpdate-Database
プロジェクト内で完結していたコードの変更を、実際のデータベースに反映させる
Update-Database -Context BloggingContext -Project Intro
すでにデータがある場合は失敗するかもしれない
実際にこの記事を書きながら、Update-Database
をしたところ、下記のようなエラーが出た
Violation of PRIMARY KEY constraint 'PK_Posts'. Cannot insert duplicate key in object 'dbo.Posts'. The duplicate key value is (4).
The statement has been terminated.
Google翻訳
PRIMARYKEY制約「PK_Posts」への違反。オブジェクト 'dbo.Posts'に重複するキーを挿入できません。重複するキー値は(4)です。
たとえば、データベースを新規作成してから、シードを与えるマイグレーションをする間に
マニュアルでレコードを挿入する操作をしていた場合
挿入した(既存の)データと、シードデータの主キーが衝突する可能性があり、上記のようなエラーが出る
Drop-Database
で解決する
この記事の前段の通り、今回はDockerコンテナのテスト環境を想定しているので、データベースを一から作り直すほうが早い
- 今からやること → データベースを削除して、まっさらな状態からシード処理が含まれている移行まで進む
- おまけ → シードデータの主キーを変える(推奨しない)
Drop-Database -Context BloggingContext -Project Intro
# Build started...
# Build succeeded.
# 確認
# この操作を実行しますか?
# 対象 "database 'Blogging' on server 'localhost, 11433'" に対して操作 "Drop-Database" を実行しています。
# [Y] はい(Y) [A] すべて続行(A) [N] いいえ(N) [L] すべて無視(L) [S] 中断(S) [?] ヘルプ (既定では "Y"):y
# Dropping database 'Blogging' on server 'localhost, 11433'.
# Successfully dropped database 'Blogging'.
データベースを削除したところで、もう1度Update-Database
再度データベースをアップデートする
-Migration
オプションで途中の移行先を指定しない限り、最後まで移行が進む
コマンドは既定で最後の移行になります。
Update-Database - Entity Framework Core ツールリファレンス
Update-Database -Context BloggingContext -Project Intro
# Build started...
# Build succeeded.
# Applying migration '20210828010156_Init'.
# Applying migration '20210903234151_Seed'.
# Done.
Init
が適応され、つづいてSeed
が適応され、順番に移行が済んだことがわかる
データベースを確認する
前回と同じ手順をたどって、データベースをテーブルを確認する
データベースに入るまで
Dockerコンテナに入って、さらにデータベースを選択する
docker exec -it sql1 "bash"
$ /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -W
1> USE Blogging;
2> GO
Changed database context to 'Blogging'.
テーブルのデータをみる
Blogs
テーブルと、Posts
テーブルのそれぞれにシードデータが存在している
1> SELECT * FROM Blogs;
2> GO
BlogId Url Rating
------ --- ------
1 http://one.com 0
2 http://two.com 0
(2 rows affected)
1> SELECT * FROM Posts;
2> GO
PostId Title Content BlogId
------ ----- ------- ------
1 title11 content11 1
2 title12 content12 1
3 title23 content23 2
4 title24 content24 2
(4 rows affected)
おわりに
DbContext
継承クラスのメソッドに数行追加することで
データベース作成時に初期データを与えることができた
おまけ
すでにデータがある状態で、シードデータを与えるとどうなるか?
既存データとマイグレーション時のシードデータが重複しないように注意してやればUpdateが通るのか?がこの章の目的
「『すでにあるデータ』のキーが既知であること」が前提であり、そんなこといちいち関知できないので。
既存のデータ
Posts
テーブル(上)とBlogs
テーブル(下)に、それぞれにデータが入っている
シードデータをあたえる
既存のデータの主キーと重複しないように、シードデータをあたえる
シードデータ:
-
BlogId
は11, 22 -
PostId
は11, 22, 33, 44
結果、既存のデータに加えて、マイグレーションによるデータシード処理がおこなわれた
つづいてマニュアルでデータを追加する
主キーを指定せず、あらたにデータを追加すると
シードデータの末尾番号からの連番で新規データが追加される
private void Button_CreateNewBlog(object sender, RoutedEventArgs e)
{
_context.Add(new Intro.Blog() { Url = $"Url{DateTime.Now.Second}" });
_context.SaveChanges();
}
private void Button_CreateNewPost(object sender, RoutedEventArgs e)
{
var blogId = _context.Blogs.OrderByDescending(x => x.BlogId).FirstOrDefault().BlogId;
_context.Add(new Intro.Post() { Title = $"PostTitle{DateTime.Now.Second}", BlogId = blogId });
_context.SaveChanges();
}
キーが衝突しなければ、データベースにデータが入っている状態でもシードデータをあたえることができるとわかった
以上。