PgAdmin4, Entity Framework Core 周り小ネタ
はじめに
PgAdmin4 や Entity Framework Core (以下 EF Core )を使ってあれこれ試しているうちに、取り立てて大きなトピックにするほどでもないけど、という程度の小ネタがたまってきた気がするので、ここにまとめてみます。
[PgAdmin4] クエリ実行
PgAdmin4 で PostgreSQL に接続して、さあ CREATE TABLE でテーブル作るぞ!と思ったらクエリ実行するウインドウらしきものがない???
と思ったら、 Database のツリーの上にありました。
なおこのウインドウはタブで開くのですが、ボタンをポチポチ押しているとその分タブが開いていきます。
過去に実行した SQL は Query History から見たり Copy All でコピーしたりできますが、タブを閉じてしまうと消えてしまうため、状況に応じて保存が必要です。
その他ツリーの Schemas から、作ったテーブルやトリガー、主キーや制約などをみることができます。
簡単な変更( Not Null にするとか)なら Property などから実行できてしまうのも便利ですね。
[PgAdmin4] バックアップとリストア
バックアップ
作った Database のバックアップ(論理)を作成するには、ツリー上にある対象の Database 上で右クリック > Backup を選択します。
FileName では保存先をファイル名まで指定します。
Format として sql 、 backup 、または all files が選択できますが、例えば sql を選んでいても拡張子として .sql が付くわけではないので注意が必要です。
注意が必要ですと書きましたが、主に影響を受けるのは後述のリストア時に Format を all files にしないと表示されない、というくらいで、バックアップやリストアの結果には関係ありません。多分。
Format ですが、バックアップ時のデフォルトは sql になっていますが、リストア時のデフォルトは backup になっているため、リストア時の手間を省くという意味で(他に理由が無ければ) backup にするのが良さそうです。
あと Encoding は UTF8 にしました。
少し引っかかったのがバックアップ結果の表示。
Stop Process の赤いボタンが気になりすぎて、「あれ?なんか失敗してる?」としばらくあれこれ確認してしまいました(;゚Д゚)
いや、 Successfully とか書いているしわかるのですけれども。
リストア
リストアのためには復元対象となる Database が必要です。
ということで空の Database を作り、ツリー上で右クリック > Restore を選択します。
こちらは FileName で先ほどのバックアップファイルを選択すれば OK です。
[EFCore] 複合主キー
例えば下記のように複合主キーを持つテーブルを参照する場合。
CREATE TABLE "Book2"( "Book2Id" Integer not null, "BookId" Bigint references "Book"("Id"), "Name" TEXT, PRIMARY KEY("Book2Id", "BookId") )
こんな感じでデータを入れます。
INSERT INTO "Book2"( "Book2Id", "BookId", "Name" ) VALUES( 0, 1, 'Bookだよ' ); INSERT INTO "Book2"( "Book2Id", "BookId", "Name" ) VALUES( 1, 1, 'Book1でした' ); INSERT INTO "Book2"( "Book2Id", "BookId", "Name" ) VALUES( 2, 2, 'Bookですか?' ); INSERT INTO "Book2"( "Book2Id", "BookId", "Name" ) VALUES( 2, 3, 'Bookだってば' );
これをこうして
Book2.cs
using System.ComponentModel.DataAnnotations.Schema; namespace EfCoreNpgsqlSample.Models { [Table("Book2")] public class Book2 { public int Book2Id { get; set; } public int BookId { get; set; } public string Name { get; set; } } }
EfCoreNpgsqlSampleContext.cs
using Microsoft.EntityFrameworkCore; namespace EfCoreNpgsqlSample.Models { public class EfCoreNpgsqlSampleContext: DbContext { public EfCoreNpgsqlSampleContext(DbContextOptions< EfCoreNpgsqlSampleContext> options) : base(options) { } public DbSet< Store> Stores { get; set; } public DbSet< Author> Authors { get; set; } public DbSet< Book> Books { get; set; } public DbSet< Book2> Books2 { get; set; } } }
こうするとどうなるか。
HomeController.cs
~省略~ foreach (Book2 book in _context.Books2) { Console.WriteLine($"ID: {book.Book2Id} BookID: {book.BookId} Name: {book.Name}"); } ~省略~
出力結果はこちらです。
ID: 0 BookID: 1 Name: Bookだよ ID: 1 BookID: 1 Name: Book1でした ID: 2 BookID: 2 Name: Bookですか? ID: 2 BookID: 2 Name: Bookですか?
。。。あれ?
Entity Framework Core では、主キーが指定されていない場合、 ~Id という名前のプロパティを主キーとして扱うようです。
今回の場合、 Book2Id がユニークのものは問題がないのですが、 4 つ目のレコードは 3 つ目と重複しているため、上記のような結果となりました。
ではどうするか。 DbContext クラスで主キーを指定してやります。
EfCoreNpgsqlSampleContext.cs
~省略~ public class EfCoreNpgsqlSampleContext: DbContext { public EfCoreNpgsqlSampleContext(DbContextOptions< EfCoreNpgsqlSampleContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity< Book2>().HasKey(b => new {b.Book2Id, b.BookId}); } public DbSet< Store> Stores { get; set; } public DbSet< Author> Authors { get; set; } public DbSet< Book> Books { get; set; } public DbSet< Book2> Books2 { get; set; } } ~省略~
これで正しい結果が得られます。
ID: 0 BookID: 1 Name: Bookだよ ID: 1 BookID: 1 Name: Book1でした ID: 2 BookID: 2 Name: Bookですか? ID: 2 BookID: 3 Name: Bookだってば
なお、 INNER JOIN で複数カラムを指定して ON としたい場合も HasKey による指定が必要となります。
※下記を見ると [Key] を複数付ける方法でも解決できそうです。