連載:ADO.NET Entity Framework入門

第3回 Entity Frameworkにおけるクエリと更新

WINGSプロジェクト 土井 毅 著/山田 祥寛 監修
2010/08/13
Page1 Page2 Page3

 前回では、多対多関係を持つEntity Data Model(以下EDM)を作成し、シンプルなデータの保存/取得を行うサンプルを実装した。今回はEntity Frameworkにおけるクエリと更新について、詳細を見ていこう。

 なお本稿では.NET Framework 4およびVisual Studio 2010をベースに解説を行っている。

Entity Frameworkでのクエリ方法

 Entity Frameworkでのクエリ方法を見ていくに当たって、まずEntity Frameworkの仕組みについて少し考えておこう。図1はEntity Frameworkがデータにアクセスする際に、ADO.NETとどのように連携するかを示している。


図1 Entity FrameworkとADO.NETとの関係
アプリケーションからクエリを行うには、Object Servicesを介する方法と、EntityClientデータ・プロバイダを直接呼び出す方法がある。Object Services経由では、「Entity SQL」「クエリ・ビルダ」「LINQ to Entities」の3つのクエリ方法がある。

 この図1から分かるように、Entity Frameworkは、ADO.NETがデータベースにアクセスする際のドライバ的な役割を持つADO.NETデータ・プロバイダの上に構築されている。そして、Entity Frameworkによりアプリケーションからクエリを行う際には、Object Servicesを介して呼び出しを行う方法と、EntityClientデータ・プロバイダを直接呼び出す方法の2種類が存在する。

 EntityClientデータ・プロバイダは、Entity Frameworkの下位のコンポーネントで、ADO.NETデータ・プロバイダを介してデータベースにアクセスする。

 一方、Object Servicesは、EntityClientデータ・プロバイダの上位に位置するコンポーネントで、データのクエリや更新を、EDMのエンティティ・クラスを基本として行う。クエリ方法には、SQLライクな言語である「Entity SQL」、メソッドの連なりでクエリを表現する「クエリ・ビルダ」、LINQを使用する「LINQ to Entities」の3つの方法がある。

 それぞれのコンポーネントによるクエリの詳細を見ていこう。

Object Servicesによるクエリ

 Object Servicesは、データのクエリや更新を行うEntity Frameworkのコンポーネントである。Object Servicesを使うことで、結果をエンティティ・クラスのインスタンスとして取得することができる。Object Servicesを使ったクエリには、前述の3種類が存在する。基本的にクエリを指定する方法が異なるだけで、結果はすべてエンティティ・クラスのインスタンスである。

■Entity SQLによるクエリ

 Entity SQLはEntity Framework用に新たに作られたSQLライクなクエリ言語である。SQL ServerのTransact-SQLによく似ているが、UPDATE、INSERT、DELETEなどの更新用のDML(Data Manipulation Language、データ操作言語)に対応していない点に注意が必要である。従って、Entity SQLを使った呼び出しで、直接データベースに更新をかけることはできない。

 なお、Entity SQLは文字列として指定するため、コーディング、コンパイル時に検証は行われない。リスト1はEntity SQLを使ったクエリのサンプルである。このサンプルは前回まで実装したアドレス帳アプリケーションのエントリを取得し、エントリに関連付けられたカテゴリも含めて一覧表示する。

private static void QueryByEntitySql()
{
  // オブジェクト・コンテキストの生成
  using (var container = new AddressBookContainer())
  {
    // Entity SQL文字列の設定
    string query =
      "SELECT VALUE a FROM AddressBookContainer.Entries AS a "
      + "WHERE a.Name = @name";

    // Entity SQLに与えるパラメータの設定
    var parameters = new System.Data.Objects.ObjectParameter[1];
    parameters[0] = new System.Data.Objects.ObjectParameter("Name", "Doi");

    // CreateQueryメソッドでクエリを呼び出す
    var entries = container.CreateQuery<Entry>(query, parameters);

    // クエリ結果を表示する
    PrintEntries(entries);
  }
}

private static void PrintEntries(IQueryable<Entry> entries)
{
  // 取得したエントリを順に表示する
  foreach (var entry in entries)
  {
    Console.WriteLine(string.Format(
      "{0} {1}歳 電話番号:{2}",
      entry.Name, entry.Age, entry.TelNo));

    // エントリに関連付けられたカテゴリも表示する
    foreach (var cat in entry.Categories)
    {
      Console.WriteLine(string.Format(
        " カテゴリ名:{0}", cat.CategoryName));
    }
  }
}
Private Sub QueryByEntitySql()

  ' オブジェクト・コンテキストの生成
  Using container As New AddressBookContainer()

    ' Entity SQL文字列の設定
    Dim query = "SELECT VALUE a FROM AddressBookContainer.Entries AS a WHERE a.Name = @name"

    ' Entity SQLに与えるパラメータの設定
    Dim parameters(1) As System.Data.Objects.ObjectParameter
    parameters(0) = New System.Data.Objects.ObjectParameter("Name", "Doi")

    ' CreateQueryメソッドでクエリを呼び出す
    Dim entries = container.CreateQuery(Of Entry)(query, parameters)

    ' クエリ結果を表示する
    PrintEntries(entries)
  End Using
End Sub

Private Sub PrintEntries(ByVal entries As IQueryable(Of Entry))

  ' 取得したエントリを順に表示する
  For Each entry In entries

    Console.WriteLine(String.Format(
      "{0} {1}歳 電話番号:{2}",
      entry.Name, entry.Age, entry.TelNo))

    ' エントリに関連付けられたカテゴリも表示する
    For Each cat In entry.Categories
      Console.WriteLine(String.Format(
      " カテゴリ名:{0}", cat.CategoryName))
    Next
  Next
End Sub
リスト1 Entity SQLによるクエリ(上:C#、下:VB)

 Object Servicesの機能は前回説明したオブジェクト・コンテキストで提供されているため、最初にこのオブジェクト・コンテキストを生成する( )。

 Entity SQLのSELECT文の形式は、一般的なSQLに似ているが、テーブルではなくエンティティ・オブジェクトに対してクエリを記述していることに注目したい。ここではEntryエンティティのNameプロパティに対してフィルタを設定している。(

 次に、オブジェクト・コンテキストのCreateQueryメソッドでクエリを呼び出す。型パラメータにEntryクラスを指定することで、Entryオブジェクトのコレクションが結果として返される( )。

 そして、取得結果を表示するPrintEntriesメソッドを呼び出す。メソッド内では、取得したエントリをforeach/For Each文を使って順に表示している。また、各エントリのCategoriesプロパティを使い、エントリに関連付けられたカテゴリも表示している。なお、Object Servicesを呼び出した結果は、どのクエリ方法でも同じになるため、PrintEntriesメソッドのコードは、ほかの2つのクエリ方法でも共通である。(

 リスト1の実行結果は以下のようになる。


図2 Entity SQLによるクエリの結果
EntryエンティティとCategoryエンティティの内容が表示されている。

 Entity SQLの詳細な仕様については、「特集:Visual Studio 2008 SP1新機能解説(2).NETの新データアクセス・テクノロジ『ADO.NET Entity Framework』」を参照いただきたい。

■クエリ・ビルダによるクエリ

 クエリ・ビルダとは、エンティティ・クラスの継承元であるObjectQueryクラスで定義されているメソッド群の総称であり、Select、Where、GroupByなど、Entity SQLの各機能に対応するメソッドが定義されている。

 クエリ・ビルダはメソッドの連なりによりEntity SQLに相当するクエリを表現するため、構文エラーなどはコンパイラが発見することができる。ただし、メソッドに与えるパラメータの内容は文字列であるため、そこにミスがあった場合には実行時まで発見されない。リスト2はクエリ・ビルダによるクエリのサンプルである。

private static void QueryByQueryBuilder()
{
  // オブジェクト・コンテキストの生成
  using (var conatiner = new AddressBookContainer())
  {
    // Query builderのWhereメソッドを呼び出す
    var entries = conatiner.Entries.Where(
                  "it.Age > @age", new ObjectParameter("age", 20));

    // クエリ結果を表示する
    PrintEntries(entries);
  }
}
Private Sub QueryByQueryBuilder()

  ' オブジェクト・コンテキストの生成
  Using container As New AddressBookContainer()

    ' Query builderのWhereメソッドを呼び出す
    Dim entries = container.Entries.Where(
                  "it.Age > @age", New ObjectParameter("age", 20))

    ' クエリ結果を表示する
    PrintEntries(entries)
  End Using
End Sub
リスト2 クエリ・ビルダによるクエリ(上:C#、下:VB)

 オブジェクト・コンテキストの生成( )および結果の表示( )はEntity SQLのサンプル(リスト1)と同じである。ここではクエリ・ビルダのメソッド群のうち、条件を指定してフィルタを行うWhereメソッドを使い、EntryエンティティのAgeプロパティが20を超えるものだけを取得している( )。

 なお、Whereメソッド呼び出しの際の引数内で指定している「it.」という謎の文字列は、取得したエンティティを表している。クエリ・ビルダの記述だけで見ると意味が分かりづらいが、クエリ・ビルダがEntity SQLに対応する機能であることを考えると、

container.Entries.Where("it.Age > @age",
                            New ObjectParameter("age", 20))

というメソッド呼び出しが、内部的には、

SELECT VALUE it
    FROM AddressBookContainer.Entries AS it
    WHERE it.Age > @age

に変換されており、「it」はエンティティの別名である、と考えることができる。なお、エンティティ・クラスのNameプロパティにより、この別名を任意に設定することができる(「it」はその既定値)。

 

 INDEX
  ADO.NET Entity Framework入門
  第3回 Entity Frameworkにおけるクエリと更新
  1.Entity SQLによるクエリ/クエリ・ビルダによるクエリ
    2.LINQ to Entitiesによるクエリ/遅延読み込み
    3.Entity Frameworkでのデータ保存/まとめ
 
インデックス・ページヘ  「ADO.NET Entity Framework入門」


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 記事ランキング

本日 月間