One-to-One 再入門(カスケードデリートとかもあるよ)
ザクです。
先の解説では、おざなりになっていたOne-to-Oneの細かい設定をやります。
まず、きっちりOne-to-Oneにする方法から行きます!
きっちりOne-to-One!
前に説明したOne-to-Oneで作ったテーブルのリレーションシップはこのようになっていたと思います。
子テーブル側のOfficeAssignmentに「0..1」と入っていますね。これは、親であるInstructorはOfficeAssignmentを子として持つ必要がないことを意味します。いわゆる、One-to-Zero or Oneというやつです。
しかし厳密にテーブルのリレーションシップを定義したい場合が多々あります。プロパティ数が多すぎるモデルをデータを取りやすいように分けるときなんかがそうですね。
では、いまあるInstructorとOfficeAssignmentをきっちりOne-to-Oneにしてみましょう。方法は2つあります。まず簡単な方から。
Required属性を使う
きっちりOne-to-Oneではモデルにナビゲーションプロパティを使えません。なので、それぞれのモデルで相手になるモデルのナビゲーションプロパティの部分の「virtual」を消して、ただのモデル参照にしてください。そして、Instructor側のOfficeAssignmentの真上に[Required]属性を付けます。これだけです。
// Instructor側 public virtual ICollection<Course> Courses { get; set; } [Required] public OfficeAssignment OfficeAssignment { get; set; } // OfficeAssignment側 public Instructor Instructor { get; set; }
上の変更は、DBのスキーマ設定の変更は発生しないので、DALにあるSchoolContext.csからダイアグラムを出力するだけで変更点が確認できます。
Fluent APIを使う
もう一つの方法はFluent APIを使う方法です。Fluent APIはDALにモデル定義を記述する場合に使います。したがって、各場所はDALのSchoolContext.csです。modelBuilder.Entity
modelBuilder.Entity<Instructor>() .HasRequired(u => u.OfficeAssignment) .WithRequiredPrincipal(o => o.Instructor);
同じくダイアグラムを出してみましょう。
きっちりOne-to-Oneになりました。
One-to-Oneをカスケードデリート
One-to-OneやOne-to-Zero or Oneの関係を定義した時、One-to-Manyの時と違い、なぜか勝手にカスケードデリートがONになりません。カスケードデリートはDBのスキーマ上で定義する必要があるので、マイグレーションのファイルの中でcascadeDeleteのフラグがtrueになっていなければなりません。カスケードデリートのフラグを立てるには、Fluent APIを使います。
//One-to-Zero or Oneの場合 modelBuilder.Entity<Instructor>() .HasOptional(i => i.OfficeAssignment) .WithRequired(o => o.Instructor) .WillCascadeOnDelete(true); //One-to-Oneの場合 modelBuilder.Entity<Instructor>() .HasRequired(i => i.OfficeAssignment) .WithRequiredPrincipal(o => o.Instructor) .WillCascadeOnDelete(true);
きっちりとそうでないのと両方のパターンを用意しました。定義に応じて、SchoolContextを上書きしてください。この変更では、DBのスキーマ変更が発生するので、書き換え後、add-migrationしてください。名前はCascadeDeleteとかでいいでしょう。
cascadeDeleteがtrueになりましたね。