実践 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 してみましょう。
するとこうなりました。
ほほぅ。ええ感じやで~
試しにこんな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 クラス)のキーのプロパティ名」の場合、自動的にそのプロパティのフィールドが外部キーとして設定されます。
それじゃ、この場合はどうやって?
ひとつは、属性を利用して、プロパティ名とは別のフィールド名を設定すればうまくいきます。
他にも方法はありますが...続きはこちらで。