連載:Entity Framework 4.1入門

第3回 Fluent APIとDbContextの機能

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

 EF 4.1連載の最終回となる今回は、「Fluent API」と呼ばれる、エンティティ・クラス外部からデータベースの構造を制御するための機能と、DbContext/DbSetクラスの使用方法について解説する。

外部からデータベース構造を設定するFluent API

 Fluent APIは、コード・ファーストにおいて、デフォルトの規約と異なるモデルを扱う際に使用するAPIである。

 前回解説した、エンティティ内に属性として記述する方法(=アノテーション)とは異なり、Fluent APIはエンティティ・クラス外部から設定を行うための仕組みだ(図1)。

図1 アノテーション(上)とFluent API(下)の違い

 Fluent APIを使用する場合、エンティティ・クラス内にEF専用のアノテーションを記述する代わりに、コンテキスト・オブジェクトにデータベースの構造を設定するための記述を行う。エンティティ内にEF用のアノテーションを記述する必要がないため、エンティティをEFに依存させない状態に保つことができる。従って、すでにエンティティ・クラスが存在し、定義に変更を加えたくない場合などに有効である。

 一方で、エンティティ・クラス自体を変更して構わない場合は、アノテーションによる記述の方が直感的で記述しやすい。Fluent APIは手続き的、アノテーションによる定義は宣言的、とも言い換えられるだろう。それぞれのプロジェクトにあった方法を選択してほしい。

Fluent APIの使用方法

 それではCompositeKeyEntityエンティティ(図2)を例に、Fluent APIの使用方法について解説していく。

図2 CompositeKeyEntityエンティティ

 CompositeKeyEntityエンティティはKey1、Key2という2つのプロパティを複合キーとして持つエンティティである。クラス定義はリスト1のようになる。

public class CompositeKeyEntity
{
  // 複合キー1つ目。アノテーションなし
  public int Key1{get;set;}

  // 複合キー2つ目。同じくアノテーションなし
  public string Key2 { get; set; }

  public string Data { get; set; }

  // フィールド名を(「備考」ではなく)「Memo」としたい
  public string 備考 { get; set; }
}
Public Class CompositeKeyEntity

  ' 複合キー1つ目。アノテーションなし
  Public Property Key1 As Integer

  ' 複合キー2つ目。同じくアノテーションなし
  Public Property Key2 As String

  Public Property Data As String

  ' フィールド名を(「備考」ではなく)「Memo」としたい
  Public Property 備考 As String
End Class
リスト1 CompositeKeyEntityエンティティのクラス定義(上:CompositeKeyEntity.cs、下:CompositeKeyEntity.vb)

 このクラス定義には、複合キーやフィールド名を設定するためのアノテーション(=前回解説したKey属性やColumn属性)が含まれていないため、このままでは主キーを特定できず、モデル・エラーが発生する。また、備考プロパティのフィールド名もそのまま「備考」となってしまう。そこで、Fluent APIを使い、Key1/Key2プロパティを複合キーに、備考プロパティのフィールド名を「Memo」に設定してみよう。

 Fluent APIは、モデル構築時に呼び出される、(コンテキスト・オブジェクトのイベント・ハンドラである)OnModelCreatingメソッド内から使用する。

 OnModelCreatingメソッドの引数は、エンティティ・クラスとデータベース定義のマッピングを設定するためのDbModelBuilderクラス(System.Data.Entity名前空間)のオブジェクトである。Fluent APIは、このDbModelBuilderオブジェクトを起点とし、メソッドを連ねて(流れるように)呼び出していくことに特徴がある*1

*1 こうしたメソッドを連ねていく手法のことを「Fluent Interface(流れるようなインターフェイス)」と呼ぶこともある。JavaScriptライブラリのjQueryなどがその代表例。

 リスト2は、CompositeKeyEntityエンティティについて、複合の主キーKey1/Key2と、「備考」プロパティのデータベース上のフィールド名を「Memo」に設定するコードである。

public class ItemCatalog : DbContext
{
  ……省略……

  // OnModelCreatingメソッドでDbModelBuilderオブジェクトを使って追加設定
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    // CompositeKeyEntityエンティティについての設定(戻り値の型:EntityTypeConfigurationクラス)
    modelBuilder.Entity<CompositeKeyEntity>()

      // 複合主キーの設定(戻り値の型:EntityTypeConfigurationクラス)
      .HasKey(t => new { t.Key1, t.Key2 })

      // 「備考」プロパティの設定(戻り値の型:StringPropertyConfigurationクラス)
      .Property(p => p.備考)

      // フィールド名の設定(戻り値の型:StringPropertyConfigurationクラス)
      .HasColumnName("Memo");
  }
  ……省略……
Public Class ItemCatalog
  Inherits DbContext

  ……省略……

  Protected Overrides Sub OnModelCreating(modelBuilder As System.Data.Entity.DbModelBuilder)

    ' CompositeKeyEntityエンティティについての設定(戻り値の型:EntityTypeConfigurationクラス)
    modelBuilder.Entity(Of CompositeKeyEntity)() _

    ' 主キーの設定(戻り値の型:EntityTypeConfigurationクラス)
    .HasKey(Function(t) New With {t.Key1, t.Key2}) _

    ' 「備考」プロパティの設定(戻り値の型:StringPropertyConfigurationクラス)
    .Property(Function(p) p.備考) _

    ' フィールド名の設定(戻り値の型:StringPropertyConfigurationクラス)
    .HasColumnName("Memo")

  End Sub
  ……省略……
リスト2 Fluent APIによる設定例(上:ItemCatalog.cs、下:ItemCatalog.vb)

 アノテーションの代わりに、メソッドの連なりでデータベースの設定を行っている点に注目してほしい。このコードは、図3のように、起点となるDbModelBuilderオブジェクトから、複数のメソッドが流れるようにして実行される。

図3 メソッドが流れるように連なっていく

 以上のようにOnModelCreatingメソッドでデータベースの構造を設定すれば、エンティティ・クラスを使用するコードはアノテーションで定義した場合とまったく同じものを使用可能である。

Fluent APIの構造

 以降は、理解を深めるためFluent APIの構造について解説する。

 Fluent APIは、メソッドを連続して呼び出すことができるよう、各メソッドが返すオブジェクトに特徴がある。

図4 DbModelBuilderオブジェクトからのメソッドによる遷移

 起点となるDbModelBuilderオブジェクトからは、図4のように、EntityメソッドやComplexTypeメソッドを呼び出すことで、エンティティと複合型の設定を行うためのEntityTypeConfigurationオブジェクトやComplexTypeConfigurationオブジェクトを取得できる。

図5 EntityTypeConfigurationオブジェクトからのメソッドによる遷移

 エンティティの設定を行うEntityTypeConfigurationオブジェクトからは、図5のように、プロパティの設定を行うためのPropertyメソッドによってXXXPropertyConfiguration(XXXの部分はプロパティのデータ型に応じて変化)オブジェクトを取得できる。

 例えば、文字列のプロパティについてはStringPropertyConfigurationオブジェクトが、日時型のプロパティについてはDateTimePropertyConfigurationオブジェクトが、Propertyメソッドの戻り値として返る。先ほどのCompositeKeyEntityエンティティの例では、備考プロパティに対応するStringPropertyConfigurationオブジェクトが返されていた。

 XXXPropertyConfigurationオブジェクトには、アノテーションで設定するのと同じような、フィールド名やデータ型の設定を行うメソッドが準備されている。先ほどの例では、フィールド名を設定するためのHasColumnNameメソッドを使用した。

 同様に、リレーションの設定を行うためのHasMany/HasRequired/HasOptionalメソッドにより、XXXNavigationPropertyConfiguration(XXXの部分はリレーションの種類に応じて変化)オブジェクトを取得できる。

 こうした仕様により、メソッドを連ねてのモデル構築を行える。表1は、Fluent APIで使用できる主要なメソッドである。

クラス メソッド 意味 戻り値の型
DbModelBuilder Entity エンティティの設定 EntityTypeConfiguration
ComplexType 複合型の設定 ComplexTypeConfiguration
Ignore クラスをデータベースと連動させない DbModelBuilder
EntityTypeConfiguration HasKey 主キーの設定 EntityTypeConfiguration
HasMany リレーションシップの設定(多) ManyNavigationPropertyConfiguration
HasRequired リレーションシップの設定(一。必須) RequiredNavigationPropertyConfiguration
HasOptional リレーションシップの設定(一。オプショナル) OptionalNavigationPropertyConfiguration
EntityTypeConfiguration, ComplexTypeConfiguration Property プロパティの設定 XXXPropertyConfiguration
(XXXはプロパティの型に合わせた名前)
Ignore プロパティをデータベースと連動させない  
XXXPropertyConfiguration IsRequired 必須設定(SQLのNOT NULL) XXXPropertyConfiguration
(自分自身のオブジェクト)
IsOptional オプショナル設定
HasColumnName フィールド名の設定
HasColumnType データ型の設定
HasMaxLength 最大長の設定(文字列型の場合)
XXXNavigationPropertyConfiguration WithMany リレーションシップの参照側の設定(多) DependentNavigationPropertyConfiguration
WithRequired リレーションシップの参照側の設定(一。必須) ForeignKeyNavigationPropertyConfiguration
WithOptionalDependent リレーションシップの参照側の設定(一。オプショナル)
DependentNavigationPropertyConfiguration HasForeignKey 外部キーの設定 CascadableNavigationPropertyConfiguration
表1 Fluent APIの主要なメソッド

 なお、DbModelBuilder/EntityTypeConfiguration/ComplexTypeConfigurationオブジェクトのIgnoreメソッドは、NotMappedアノテーションに相当するもので、指定したエンティティやプロパティをデータベースと関連付けない、という設定を行うためのメソッドである。

Fluent APIの設定例

 Fluent APIは多数のメソッドやクラスを用意しているため、すべてを紹介することはできない。詳細については、MSDNの「DbModelBuilderオブジェクトのページ」を起点に確認してほしい。また、Visual StudioのIntelliSenseはFluent APIと相性がよく、慣れれば自然にメソッドを連ねて記述できるだろう。

 参考までに、リスト3にFluent APIによるいくつかの設定例を示す。ここでは、1対多関係のリレーションシップや多対多関係のリレーションシップをFluent APIを使って記述している。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  // MemberとItemの1対多リレーションの設定
  modelBuilder.Entity<Member>()
    // HasManyメソッドで複数のItemを持つリレーションを設定
    .HasMany(ct => ct.Items)
    // 逆向きのリレーションを必須設定
    .WithRequired(item => item.Member)
    // 外部キーはItem.会員Idプロパティとする
    .HasForeignKey(item => item.会員Id);

  // ItemとCategoryの多対多リレーションの設定
  modelBuilder.Entity<Item>()
    // HasManyメソッドでItem -> Categoryのリレーションを多重度多で設定
    .HasMany<Category>(t => t.Categories)
    // 逆向きのリレーションも多重度多で設定→多対多リレーションになる
    .WithMany(t => t.Items);

  // 基底クラスのOnModelCreatingメソッド呼び出し
  base.OnModelCreating(modelBuilder);
}
Protected Overrides Sub OnModelCreating(
  modelBuilder As System.Data.Entity.DbModelBuilder)

  ' MemberとItemの1対多リレーションの設定
  modelBuilder.Entity(Of Member)() _
    ' HasManyメソッドで複数のItemを持つリレーションを設定
    .HasMany(Function(ct) ct.Items) _
    ' 逆向きのリレーションを必須設定
    .WithRequired(Function(i) i.Member) _
    ' 外部キーはItem.会員Idプロパティとする
    .HasForeignKey(Function(i) i.会員Id)

  ' ItemとCategoryの多対多リレーションの設定
  modelBuilder.Entity(Of Item)() _
    ' HasManyメソッドでItem -> Categoryのリレーションを多重度多で設定
    .HasMany(Of Category)(Function(t) t.Categories) _
    ' 逆向きのリレーションも多重度多で設定→多対多リレーションになる
    .WithMany(Function(t) t.Items)

  ' 基底クラスのOnModelCreatingメソッド呼び出し
  MyBase.OnModelCreating(modelBuilder)
End Sub
リスト3 Fluent APIによるリレーションの設定例(上:ItemCatalog.cs、下:ItemCatalog.vb)

 これらのコードにより、ItemとMemberの1対多リレーションシップについては図6のように、ItemとCategoryの多対多リレーションシップについては図7のようなデータベース構造となる。

図6 ItemとMemberの1対多リレーションシップ

図 7ItemとCategoryの多対多リレーションシップ


 INDEX
  [連載]Entity Framework 4.1によるコード・ファースト開発
  第3回 Fluent APIとDbContextの機能
  1.外部からデータベース構造を設定するFluent API
    2.DbContext、DbSetクラスの使用方法

インデックス・ページヘ  「連載: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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

Insider.NET 記事ランキング

本日 月間
ソリューションFLASH