インクリメンタルなカイハツにっき

.NET 開発手法を中心に、これから始める方の一助となる記事を載せていく予定です。

実践 Entity Framework ~ 外部キーを設定する

Entity Framework で Code First Migrations。

前回、3つ程コードファーストを利用して、テーブルを作成しようとしてみました。

単にテーブルを作成するのではなく、関連をもった(リレーションシップが設定された)テーブルを作成してみたいと思います。
目標はこんな感じ。

リレーションシップ

Prefectures(都道府県)と Municipalities(地方自治体)が1対多の関係。親子でいうと、Prefecture が親に対して、Minicipality が子の関係ですね。この関連を作るために、Minicipalities テーブルの PrefectureCode フィールドに外部キーを設定したいと思います。PrefectureCode には、Prefectures テーブルの Code 以外は許可しないというやつです。

ナビゲーションプロパティを設定

Municipality クラスに、参照元となる Prefecture クラスを型とした、次のプロパティを追加します。

    public Prefecture Prefecture { get; set; }

クラス全体はこうなります。

public class Municipality
{
    [Key]
    [MaxLength(6)]
    public string Code { get; set; }

    [MaxLength(5)]
    public string PrefectureCode { get; set; }

    [MaxLength(20)]
    public string Name { get; set; }

    [MaxLength(40)]
    public string Kana { get; set; }

    public Prefecture Prefecture { get; set; } // 追加分
}

こんなんでええんか?...とりあえず、Add-Migration してみます。
するとこんな感じに。

public override void Up()
{
    CreateTable(
        "dbo.Municipalities",
        c => new
            {
                Code = c.String(nullable: false, maxLength: 6),
                PrefectureCode = c.String(maxLength: 5),
                Name = c.String(maxLength: 20),
                Kana = c.String(maxLength: 40),
            })
        .PrimaryKey(t => t.Code)
        .ForeignKey("dbo.Prefectures", t => t.PrefectureCode)
        .Index(t => t.PrefectureCode);

    ... 以下略

コードの詳細は、今後気が向いたら解説するかもしれませんが、なんとなく「ForeignKey」とか「PrefectureCode」とか、うまくいきそうな単語が出現しております。

失敗を恐れずに、Update-Database してみましょう。
するとこうなりました。

Created ForeignKey

ほほぅ。ええ感じやで~
試しにこんなSQLを実行してみると...

INSERT INTO Municipalities(Code, PrefectureCode)
VALUES('50101', '50')

お答えは

INSERT ステートメントは FOREIGN KEY 制約 "FK_dbo.Municipalities_dbo.Prefectures_PrefectureCode" と競合しています。競合が発生したのは、データベース "AddressData"、テーブル "dbo.Prefectures", column 'Code' です。

Prefectures テーブルに 「50」なんてコードはない!と怒られました。無事成功ですね。
でも、これだけで本当にいいの?なんで?

外部キーの命名規約

プロパティをひとつ追加しただけでうまくいった理由は、プロパティ名のつけ方にあります。プロパティ名を教科書通りな名称としているからです。

試しに一旦戻しましょう。こんな要望が来たとします。

「PrefectureCode なんて長すぎるよ~。PCD とかにしてよ~」

...なんやねん、それ。まぁそれじゃ、Minicipalities クラスの PrefectureCode プロパティを PCD にしちゃいましょう。

public string PCD { ge; set; }

これで、Add-Migration すると...

public override void Up()
{
    CreateTable(
        "dbo.Municipalities",
        c => new
            {
                Code = c.String(nullable: false, maxLength: 6),
                PCD = c.String(maxLength: 5),
                Name = c.String(maxLength: 20),
                Kana = c.String(maxLength: 40),
                Prefecture_Code = c.String(maxLength: 5),
            })
        .PrimaryKey(t => t.Code)
        .ForeignKey("dbo.Prefectures", t => t.Prefecture_Code)
        .Index(t => t.Prefecture_Code);
    
    ... 以下略

??
そうです。我々は既に知ってしまっています。これが先ほどのコードと明らかに異なることに...
変更した PCD フィールド以外に、作ってもいない Prefecture_Code があるではないですか!お前は誰や!?

要するに、プロパティ名が、「外部キー用のプロパティ名(ちょっとおかしな説明ですがここでは Municipality クラスの Prefecture プロパティのことです。ちなみにこのプロパティはナビゲーションプロパティと呼ばれています)」+「参照される側(Prefecture クラス)のキーのプロパティ名」の場合、自動的にそのプロパティのフィールドが外部キーとして設定されます。

それじゃ、この場合はどうやって?

ひとつは、属性を利用して、プロパティ名とは別のフィールド名を設定すればうまくいきます。

他にも方法はありますが...続きはこちらで。