連載:Entity Framework 4.1入門

第2回 EF 4.1の規約とデータベースの初期化方法

WINGSプロジェクト 土井 毅(監修:山田 祥寛)
2011/07/13
Page1 Page2 Page3

リレーションシップと多重度

 ほかのエンティティ・クラスを参照するプロパティはナビゲーション・プロパティと見なされ、テーブル間のリレーションシップが作成される。

 リレーションシップの多重度は、ナビゲーション・プロパティの型に基づいて設定される。リスト8のように、シンプルに別のエンティティ・クラスを参照した場合は多重度が「1」に、ICollectionインターフェイスでほかのエンティティ・クラスを参照すると多重度が「多」になる。

---Item.cs

public class Item
{
  ……省略……
  public virtual Member Member { get; set; } // 会員
}
---Member.cs

public class Member
{
  ……省略……
  public virtual ICollection<Item> Items { get; set; } // 商品一覧
}
---Item.vb

Public Class Item
  ……省略……
  Public Overridable Property Member As Member ' 会員

End Class
---Member.vb

Public Class Member

  ……省略……
  Public Overridable Property Items As ICollection(Of Item) ' 商品一覧

End Class
リスト8 1対多のリレーションシップ(上1:Item.cs、上2:Member.cs、下1:Item.vb、下2:Member.vb)

 以上のコードでは、図3のような一対多関係のリレーションシップが生成される。

図3 一対多関係のリレーションシップ

 コード・ファーストでは、多対多のリレーションシップもサポートされており、リレーションシップの両端でお互いをICollectionインターフェイスとして定義することで、多対多のリレーションシップが作成される。多対多関係の場合、データベース上では中間テーブルが生成されるが、プログラム上では中間テーブルを意識することはない。

 リスト9は、商品と商品の持つカテゴリを多対多関係で表したもので、商品を表すItemエンティティ・クラスと、カテゴリを表すCategoryエンティティ・クラスで、お互いをICollectionインターフェイスで参照している。

---Item.cs

public class Item
{
  ……省略……
  // カテゴリ一覧
  public virtual ICollection<Category> Categories{ get; set; }
}
---Category.cs

public class Category
{
  public int Id { get; set; } // カテゴリID
  public string Name { get; set; } // カテゴリ名

  // カテゴリ内商品一覧
  public virtual ICollection<Item> Items{ get; set; }
}
---Item.vb

Public Class Item
  ……省略……
  ' カテゴリ一覧
  Public Overridable Property Categories As ICollection(Of Category)

End Class
---Category.vb

Public Class Category

  Public Property Id As Integer ' カテゴリID
  Public Property Name As String ' カテゴリ名

  ' カテゴリ内商品一覧
  Public Overridable Property Items As ICollection(Of Item)

End Class
リスト9 多対多関係のリレーションシップ(上1:Item.cs、上2: Category.cs、下1:Item.vb、下2:Category.vb)

 実際のデータベースは図4のように、ItemsテーブルとCategoriesテーブルに加え、中間テーブルとしてCategoryItemsというテーブルが自動生成されている。

図4 自動生成された中間テーブル

外部キー

 リレーションシップが定義されている場合、以下のいずれかの名前を持ち、リレーション先の主キー・プロパティと同じ型を持つプロパティは、リレーションシップの外部キーと見なされる。

  1. <ナビゲーション・プロパティ名><リレーション先の主キー・プロパティ名> (例:ナビゲーション・プロパティ「Member」+主キー・プロパティ「Id」=「MemberId」)

  2. <リレーション先のクラス名><リレーション先の主キー・プロパティ名> (例:リレーション先のクラス名「住所」+主キー・プロパティ「JushoId」=「住所JushoId」)

  3. <リレーション先の主キー・プロパティ名> (例:主キー・プロパティ「JushoId」)

 複数の候補がある場合は、上記の順で選択される。例えばリスト10では、ナビゲーション・プロパティ名である「Member」に、Memberエンティティ・クラスの主キーである「Id」を追加した「MemberId」という名前のフィールドが、自動的に外部キーとして定義される。

public class Item
{
  ……省略……
  // 自動的に選択された外部キー
  public int MemberId { get; set; } // 会員ID
  public virtual Member Member { get; set; } // 会員
}
Public Class Item

  ……省略……
  ' 自動的に選択された外部キー
  Public Property MemberId As Integer ' 会員ID
  Public Overridable Property Member As Member ' 会員
End Class
リスト10 外部キーの自動的な指定(上:Item.cs、下:Item.vb)

 リスト11のようにForeignKey属性を使用することで、命名則に沿わないプロパティを外部キーにも設定できる。ここではForeignKey属性を指定することで、「住所キー」プロパティを、住所エンティティへのナビゲーション・プロパティ「Address」に対する外部キーとして設定している。ForeignKey属性の引数には、対象とするナビゲーション・プロパティ名(ここでは「Address」)を指定する。

public class Member
{
  ……省略……
  // 住所エンティティへのリレーションシップ
  public 住所 Address { get; set; }
 
  // Addressプロパティへの外部キーとして定義
  [ForeignKey("Address")]
  public int 住所キー { get; set; } // 外部キー
}
Public Class Member
  ……省略……

  ' 住所エンティティへのリレーションシップ
  Public Property Address As 住所

  ' Addressプロパティへの外部キーとして定義
  <ForeignKey("Address")>
  Public Property 住所キー As Integer

End Class
リスト11 外部キーの明示的な指定(上:Member.cs、下:Member.vb)

 この場合、図5のように、「住所キー」フィールドが外部キー(図中では「FK1」と表記)として用いられる。

図5 「住所キー」フィールドが外部キーとして使用される

 なお、外部キーが指定されなかった場合、

<ナビゲーション・プロパティ名>_<リレーション先の主キー・プロパティ名>

という名前で外部キーが自動生成される(例:「Member_Id」)。

 ここで、名前の中に「_(アンダースコア)」が含まれることに注意してほしい。この名称は最初に挙げた外部キーのルールに合っておらず、自動生成されるのと同じアンダースコアを含むプロパティを定義しても、外部キーとして自動的に選択されることはない点も、やや注意が必要である。

複合型

 複合型とは、複数のプロパティで構成されるデータ型のことである。複合型はデータベース上では個別のテーブルではなく、テーブルのいくつかのフィールドにマッピングされる。多数のフィールドを持つテーブルをモデル化する際に、関連するフィールドをまとめて複合型として定義することで、モデルのプロパティが乱立するのを避けられる。複合型の詳細、エンティティとの違いについては「連載:ADO.NET Entity Framework入門 第4回 データベースからのEntity Data Model生成」を参照のこと。

 コード・ファーストのデフォルトでは、主キー・プロパティとナビゲーション・プロパティを持たないクラスは複合型と見なされ、参照元のテーブルのフィールドにマッピングされる。

 リスト12では、姓名を表す「NameType」という名前の複合型を定義し、Memberクラスから参照している。

---NameType.cs
// 主キーが存在しないので複合型になる
public class NameType
{
  // 明示的にフィールド名を指定
  [Column("FirstName")]
  public string FirstName { get; set; }

  public string LastName { get; set; }
}

……省略……
---Member.cs

public class Member
{
  // 複合型を参照
  public NameType Name { get; set; }
……省略……
---NameType.vb
' 主キーが存在しないので複合型になる
Public Class NameType

  ' 明示的にフィールド名を指定
  <Column("FirstName")>
  Public Property FirstName As String

  Public Property LastName As String

End Class
---Member.vb

Public Class Member

  ' 複合型を参照
  Public Property Name As NameType
……省略……
リスト12 (上1:NameType.cs、上2:Member.cs、下1:NameType.vb、下2:Member.vb)

 NameTypeクラスには主キーに相当するプロパティがないため、複合型と見なされ、図6のように、MembersテーブルのName_LastNameフィールドとFirstNameフィールドに関連付けられる。

図6 複合型のマッピング

 なお、複合型のプロパティのフィールド名は、デフォルトでは「Name_LastName」のように、

<複合型を参照しているプロパティ名>_<複合型のプロパティ名>

という形式で自動生成される。リスト13のFirstNameプロパティのように、Column属性を使うことで、明示的にフィールド名を指定できる。自動生成されるフィールド名はやや使いづらいため、明示的にフィールド名を指定することをオススメする。

遅延ローディング

 ナビゲーション・プロパティがvirtual(C#)/Overridable(VB)キーワードで修飾されている場合、リレーション先のエンティティは遅延ロードされる。

 遅延ロードとは、ナビゲーション・プロパティの先のエンティティを、実際にそのプロパティへのアクセスが行われた時点で読み込む方式である。遅延ロードを行うことにより、実際にアクセスするデータだけが読み込まれるため、データベース負荷を低減させられる。

マッピングしないプロパティ

 エンティティ・クラスのプロパティであるものの、データベース上では扱う必要がない場合は、リスト13のようにNotMapped属性を付加することで、テーブルのフィールドへのマッピングが行われなくなる。

public class Item
{
  ……省略……
  // マッピング不要のプロパティ
  [NotMapped]
  public string internalMemo { get; set; }
}
Public Class Item
  ……省略……

  ' マッピング不要のプロパティ
  <NotMapped()>
  Public Property internalMemo As String
End Class
リスト13 マッピングしないプロパティの指定(上:Item.cs、下:Item.vb)

【コラム】検証用の属性

 System.ComponentModel.DataAnnotations名前空間には、ここで解説した属性のほかに、データの検証を行うためのいくつかの属性が用意されている。これらの属性とASP.NET MVC 3を組み合わせることで、属性(アノテーション)に基づく入力検証を実装できる。

 データ検証の詳細については、「連載:ASP.NET MVC入門【バージョン3対応】 第3回 モデル・バインドとアノテーション検証の実装」を参照のこと。



 INDEX
  [連載]Entity Framework 4.1入門
  第2回 EF 4.1の規約とデータベースの初期化方法
    1.Entity Framework 4.1の規約とカスタマイズ方法(1)
  2.Entity Framework 4.1の規約とカスタマイズ方法(2)
    3.データベースの初期化

インデックス・ページヘ  「連載:Entity Framework 4.1入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間