1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【EF Core】移行をつかってデータベースの列を追加・更新・削除する

Posted at

この記事は

手を動かしながら Entity Framwwork Core の移行機能を体感するために
「モデルを更新 → Add-Migration → モデルを更新 → ・・・」を繰り返した記録である

この記事の前提条件

検証につかうBlogsテーブルの説明

その名の通り、ブログを管理するためのテーブル

今回は、このBlogsテーブルを対象にして
列を追加したり、名前を変えたり、削除したり、、を行う

テーブルの構造

3つのフィールドから構成される

image.png

※ 画像は Visual Studio 2019 - SQL Server オブジェクトエクスプローラー のキャプチャ

テーブルの中身

過去におこなったSeed移行により、2つのレコードが挿入されている

image.png

(復習)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が存在する場合は明示する必要がある

参考:EF Core ツールリファレンス

移行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が割り当てられている

image.png

移行2;列の名前を変える

IsPrivateの既定値がFalseであることがわかったので
IsPrivateの名前をIsPublicに変更する暴挙に出る

データがまだ入ってないなら、移行を削除してやり直すほうがスマートだけど、検証なので・・・

列名を変更するの前と後で、すでにある値が変わらないことを確認するために
テーブルの中の値を一か所だけTrueにしておく

image.png

2行目のIsPrivateTrueにした

モデルを変更する

モデルのIsPrivateIsPublicに変更する

 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列目のIsPrivateIsPublicに変更された
  • 2行目のIsPublicTrueのまま保持されている

image.png

移行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列になった(この記事の最初の状態に戻った)

image.png

移行4;すでにNULL値が入っているフィールドに対して、NotNull属性を与えてみる

冒頭でも載せたとおり、テーブルの構造は以下のようになっている

image.png

UrlNullを許容されている

この状態で**UrlNULLであるレコードを挿入しておき、あとからUrlにNotNull属性を与えて移行するとどうなるか**を試す

新しいレコードを追加

UrlNULLである3行目を追加した

image.png

モデルを変更する

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だったところに値を入れる

image.png

再度データベースを更新すると、成功した。

パッケージマネージャーコンソール
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では無理っぽいことが書いてある。

image.png

T-SQLってなんだっけ?

無意識につかっていたけど、SQL Server でつかっている SQL を T-SQL と呼ぶらしい

T-SQL (Transact-SQL) とは Microsoft と Sybase 開発した、スタンダードの SQL (Structured Query Languag) を拡張した言語で、Microsoft SQL Server で使われています。

引用元:T-SQL 入門 - SQL Server 入門

おわりに

今回は、モデルを変更して、データベースに変更を反映させる操作を4回繰り返した

基本的な作成・更新・削除の動作は確認することができた

参考にさせていただいた記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?