technoshop

杉並区和泉のソフトウェアハウス、株式会社テラソフトの技術ブログです。主に.NET MVCとKnockoutJSの情報をまとめます。

One-to-One 再入門(カスケードデリートとかもあるよ)

ザクです。

先の解説では、おざなりになっていたOne-to-Oneの細かい設定をやります。

まず、きっちりOne-to-Oneにする方法から行きます!

きっちりOne-to-One!

前に説明したOne-to-Oneで作ったテーブルのリレーションシップはこのようになっていたと思います。

f:id:technoshop:20140924164234j:plain

子テーブル側の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()の上に、下のコードを追加します。(Fluent APIでリレーションシップを定義する場合は、先の[Required]を消しておいてください)

  modelBuilder.Entity<Instructor>()
        .HasRequired(u => u.OfficeAssignment)
        .WithRequiredPrincipal(o => o.Instructor);

同じくダイアグラムを出してみましょう。

f:id:technoshop:20140924171242j:plain

きっちり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とかでいいでしょう。

f:id:technoshop:20140924172531j:plain

cascadeDeleteがtrueになりましたね。