この記事は
手を動かしながら Entity Framwwork Core の移行機能を体感するために
「モデルを更新 → Add-Migration
→ モデルを更新 → ・・・」を繰り返した記録である
この記事の前提条件
- この記事の前段となる記事がある
-
公式ドキュメントのモデルを参考にしてつくった
Blog
クラスがある - プロジェクトには、すでに2つの移行が存在する
- はじめての移行である
Init
移行と、テーブルに初期値を与えてたSeed
移行がすでに追加されている
- はじめての移行である
検証につかうBlogs
テーブルの説明
その名の通り、ブログを管理するためのテーブル
今回は、このBlogs
テーブルを対象にして
列を追加したり、名前を変えたり、削除したり、、を行う
テーブルの構造
3つのフィールドから構成される
※ 画像は Visual Studio 2019 - SQL Server オブジェクトエクスプローラー のキャプチャ
テーブルの中身
過去におこなったSeed
移行により、2つのレコードが挿入されている
(復習)EF Core コマンドの使い方
上の2つは、移行を進めるために使うコマンド
下の2つは、なにか間違いがあったときに巻き戻すためのコマンド
# 移行を追加する
Add-Migration -Name {移行の名前} -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
# 最後に移行までデータベースを更新する(途中までの以降は -Migration オプションで指定可能)
Update-Database -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
# ----上:移行を進める-----下:なにか間違ったときにつかう----------------------------------------
# 最後の移行を削除する
Remove-Migration -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
# データベースを削除する
Drop-Database -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
末尾の-Context BloggingContext -Project Intro -StartupProject WPF_EFCore
は共通パラメータである
-
-Project
と-StartupProject
は、Visual Studio 側で設定していれば省略可能 -
-Context
は、プロジェクト内に複数のDbContext
が存在する場合は明示する必要がある
移行1;新しく列を追加する
ブログの公開状況を管理するIsPrivate
列を追加する
モデルを変更する
モデルにIsPrivate
フィールドを追加する
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
+ public bool IsPrivate { get; set; }
public List<Post> Posts { get; set; }
}
移行を追加するAdd-Migration
Add-Migration -Name Add_NewColumn_IsPrivate -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
データベースを更新Update-Database
の結果
- 4列目に
IsPrivate
が追加された - 値はすべて
False
が割り当てられている
移行2;列の名前を変える
IsPrivate
の既定値がFalse
であることがわかったので
IsPrivate
の名前をIsPublic
に変更する暴挙に出る
データがまだ入ってないなら、移行を削除してやり直すほうがスマートだけど、検証なので・・・
列名を変更するの前と後で、すでにある値が変わらないことを確認するために
テーブルの中の値を一か所だけTrue
にしておく
2行目のIsPrivate
をTrue
にした
モデルを変更する
モデルのIsPrivate
をIsPublic
に変更する
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
- public bool IsPrivate { get; set; }
+ public bool IsPublic { get; set; }
public List<Post> Posts { get; set; }
}
移行を追加するAdd-Migration
Add-Migration -Name Rename_IsPrivate_to_IsPublic -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
移行の内容を確認する
公式ドキュメントの列名の変更によると
EF Core は、通常、列を削除し、新しい列 (2 つの異なる変更) を作成し、列の名前を変更する必要があるかどうかを知ることができません。
ということらしく、自分で移行の内容を書き換える必要があることが示唆されている
しかし、実際に生成されたUP
メソッドは以下であり
どうやら自分が検証した環境では書き換える必要がなさそう
migrationBuilder.RenameColumn(
name: "IsPrivate",
table: "Blogs",
newName: "IsPublic");
EF CoreのバージョンはMicrosoft.EntityFrameworkCore 5.0.9
をつかった
データベースを更新Update-Database
の結果
期待したとおりの結果になった
- 4列目の
IsPrivate
がIsPublic
に変更された - 2行目の
IsPublic
がTrue
のまま保持されている
移行3;列を削除する
よくよく考えると、ブログは公開するものなので、IsPublic
列を削除する
公開/非公開は記事ごとに区別するケースが多そうなので、Post
モデルに与えるほうがよい
モデルを変更する
モデルのIsPublic
フィールドを削除する
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
- public bool IsPublic { get; set; }
public List<Post> Posts { get; set; }
}
移行を追加するAdd-Migration
Add-Migration -Name Remove_IsPublic -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
データベースを更新Update-Database
の結果
IsPublic
列が削除され、3列になった(この記事の最初の状態に戻った)
移行4;すでにNULL値が入っているフィールドに対して、NotNull属性を与えてみる
冒頭でも載せたとおり、テーブルの構造は以下のようになっている
Url
はNullを許容
されている
この状態で**Url
がNULL
であるレコードを挿入しておき、あとからUrl
にNotNull属性を与えて移行するとどうなるか**を試す
新しいレコードを追加
Url
がNULL
である3行目を追加した
モデルを変更する
Url
のNULLを許容しないように、Required
属性をつける
public class Blog
{
public int BlogId { get; set; }
+ [Required]
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
移行を追加するAdd-Migration
Add-Migration -Name Add_NotNullto_Url -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
データベースを更新Update-Database
の結果
失敗した
Url
列にNULL
は入れちゃだめよ、と。
Cannot insert the value NULL into column 'Url', table 'Blogging.dbo.Blogs'; column does not allow nulls. UPDATE fails.
The statement has been terminated.
成功するようにデータを変更する
NULL
だったところに値を入れる
再度データベースを更新すると、成功した。
Update-Database -Context BloggingContext -Project Intro -StartupProject WPF_EFCore
Build started...
Build succeeded.
Applying migration '20210912004324_Add_NotNullto_Url'.
以降5;列の順番を変え・・・られなかった
いろいろ試してみたけど、今の自分には無理だった
試しにやってみたけど、ダメだった例
単純にモデルのプロパティ順序を入れ替えるだけでは、列順は入れ替わらない
protected override void Up(MigrationBuilder migrationBuilder)
もDown
も空っぽ
public class Blog
{
public int BlogId { get; set; }
- public string Url { get; set; }
- public int Rating { get; set; }
+ public int Rating { get; set; }
+ public string Url { get; set; }
public List<Post> Posts { get; set; }
}
公式ドキュメントに書いてある注意事項
「SQL Server 列の順番」で Google 検索すると、ちゃんと公式ドキュメントが最初にヒットする
列の順番に依存するアプリケーションに影響を及ぼすかもしれないので
もし列の順番を変更するなら、十分注意してやってください、と。
またアプリケーション側に対しても要請が出ており
- 列の順番はアプリケーション側で面倒を見てください
- データベースの列の順番に依存しないようにするためにも、
SELECT *
に頼らないでください
と書かれている。
さらに、とどめの一撃として
このタスク(列の順序を変更すること)は、T-SQLでは無理っぽいことが書いてある。
T-SQLってなんだっけ?
無意識につかっていたけど、SQL Server でつかっている SQL を T-SQL と呼ぶらしい
T-SQL (Transact-SQL) とは Microsoft と Sybase 開発した、スタンダードの SQL (Structured Query Languag) を拡張した言語で、Microsoft SQL Server で使われています。
おわりに
今回は、モデルを変更して、データベースに変更を反映させる操作を4回繰り返した
基本的な作成・更新・削除の動作は確認することができた
参考にさせていただいた記事