vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

PgAdmin4, Entity Framework Core 周り小ネタ

はじめに

PgAdmin4 や Entity Framework Core (以下 EF Core )を使ってあれこれ試しているうちに、取り立てて大きなトピックにするほどでもないけど、という程度の小ネタがたまってきた気がするので、ここにまとめてみます。

[PgAdmin4] クエリ実行

PgAdmin4 で PostgreSQL に接続して、さあ CREATE TABLE でテーブル作るぞ!と思ったらクエリ実行するウインドウらしきものがない???

と思ったら、 Database のツリーの上にありました。

f:id:mslGt:20190326091317j:plain

なおこのウインドウはタブで開くのですが、ボタンをポチポチ押しているとその分タブが開いていきます。

過去に実行した SQL は Query History から見たり Copy All でコピーしたりできますが、タブを閉じてしまうと消えてしまうため、状況に応じて保存が必要です。

その他ツリーの Schemas から、作ったテーブルやトリガー、主キーや制約などをみることができます。

f:id:mslGt:20190326091343j:plain

簡単な変更( Not Null にするとか)なら Property などから実行できてしまうのも便利ですね。

[PgAdmin4] バックアップとリストア

バックアップ

作った Database のバックアップ(論理)を作成するには、ツリー上にある対象の Database 上で右クリック > Backup を選択します。

FileName では保存先をファイル名まで指定します。

f:id:mslGt:20190326091407j:plain

Format として sql 、 backup 、または all files が選択できますが、例えば sql を選んでいても拡張子として .sql が付くわけではないので注意が必要です。

注意が必要ですと書きましたが、主に影響を受けるのは後述のリストア時に Format を all files にしないと表示されない、というくらいで、バックアップやリストアの結果には関係ありません。多分。

Format ですが、バックアップ時のデフォルトは sql になっていますが、リストア時のデフォルトは backup になっているため、リストア時の手間を省くという意味で(他に理由が無ければ) backup にするのが良さそうです。

あと Encoding は UTF8 にしました。

少し引っかかったのがバックアップ結果の表示。

f:id:mslGt:20190326091432j:plain

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] を複数付ける方法でも解決できそうです。

参照