連載:ADO.NET Entity Framework入門

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

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

■LINQ to Entitiesによるクエリ

 LINQ to Entitiesは、.NET Framework 3.5より導入されたLINQ(言語統合クエリ)を、EDMに対して行う機能である。C#やVBなどの言語に統合されているため、LINQ to SQLなどのほかのLINQ技術と同様に、Visual StudioによるIntelliSense機能や、コンパイル時の構文チェックなど、コーディングのサポートを受けることができる。なお、ほかのLINQ技術と同様、更新機能は持っていない。

 次のリスト3はLINQ to Entitiesによるクエリのサンプルである。

private static void QueryByLinq()
{
  // オブジェクト・コンテキストの生成
  using (var container = new AddressBookContainer())
  {
    string name = "Doi";

    // LINQ to Entitiesでクエリを実行
    var entries = from entry in container.Entries
                  where entry.Name == name
                  select entry;

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

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

    Dim name = "Doi"

    ' LINQ to Entitiesでクエリを実行
    Dim entries = From entry In container.Entries
                  Where entry.Name = name
                  Select entry

    ' クエリ結果を表示する
    PrintEntries(entries)
  End Using
End Sub
リスト3 LINQ to Entitiesによるクエリ(上:C#、下:VB)

 やはりオブジェクト・コンテキストの生成( )および結果の表示( )はこれまでと同様である。ここではLINQ to Entitiesにより、Nameプロパティにフィルタを掛けて実行している( )。

 LINQ to Entitiesは、(LINQ構文への慣れは必要なものの)記述も簡潔で、コーディング時の検証もしっかりしているため、積極的に活用していきたい。ただし、LINQはコンパイル時にクエリを完成させるため、実行時に任意のクエリを動的に作成すること(アドホック・クエリと呼ぶ)は難しく、基本的には固定的なクエリを実行するための方法である。

[コラム]動的なLINQ生成

 DynamicQueryと呼ばれるライブラリを利用することで、LINQを実行時に文字列から動的に作成する方法が存在するようである。興味のある方は以下のページを参照されたい。

ScottGu's Blog:
Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)

 ただし、

「文字列からLINQを動的に作成する」=「コンパイラの検証は行えない」

という二律背反な面もあるため、アドホック・クエリを行う必要がある場合は、基本的にEntity SQLやクエリ・ビルダを使うことを検討してほしい。

■遅延読み込み

 ここまでのObject Servicesを使ったサンプルで、Entity SQLやLINQクエリ内で指定したEntryエンティティの内容だけでなく、指定していないCategoryエンティティの内容まで表示されていることに注目してほしい。これは、.NET Framework 4からのEntity Frameworkの新機能で、遅延読み込み(Lazy-loading)と呼ばれる機能である。

 .NET Framework 3.5のEntity Frameworkでは、この遅延読み込み機能が存在しなかったため、使用するエンティティを明示的に読み込む必要があった。リスト4は、.NET Framework 3.5での、関連するエンティティを読み込むLoadメソッドを使ったサンプルである。

foreach (var entry in entries) // Entryエンティティを順に列挙
{
  Console.WriteLine(string.Format(
    "{0} {1}歳 電話番号:{2}",
    entry.Name, entry.Age, entry.TelNo));

  // Loadメソッドを使い、明示的にCategoryエンティティを読み込む
  entry.Categories.Load();

  foreach (var cat in entry.Categories)
  {
    Console.WriteLine(string.Format(
      " カテゴリ名:{0}", cat.CategoryName));
  }
}
For Each entry In entries ' Entryエンティティを順に列挙

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

  ' Loadメソッドを使い、明示的にCategoryエンティティを読み込む
  entry.Categories.Load()

  For Each cat In entry.Categories
    Console.WriteLine(
      String.Format(" カテゴリ名:{0}", cat.CategoryName))
  Next
Next
リスト4 Loadメソッドを使った明示的な読み込み(.NET Framework 3.5版)

 .NET Framework 4からは、リスト1のサンプルのように、ほかのエンティティへのアソシエーション(ここではEntryエンティティのCategoriesプロパティ)にアクセスすることで、遅延読み込みが自動的に行われ、値を適切に取得することができるようになった*1

*1 使用するエンティティを含め、最初にまとめて読み込む方法はEager-loadingと呼ぶ。定まった和訳はないようだが、「Lazy-loading」=「実際に必要になるまで読み込みを怠ける」に対し、「Eager-loading」=「最初から熱心に読み込みを行う」といった意味合いを持つ(「lazy:怠惰な」は「eager:熱心な」の反対語)。

 遅延読み込みを行うかどうかは、エンティティ・コンテナ(リスト1〜3ではAddressBookContainer)のContextOptionsプロパティのLazyLoadingEnabledプロパティで指定できる。

 このプロパティの本来のデフォルト値は、False(遅延読み込みを行わない=.NET Framework 3.5と同じ挙動)であるが、Visual Studio 2010でADO.NET Entity Data Modelツールを使ってEDMを作成した場合、デフォルトでこのプロパティにTrue(遅延読み込みを行う)を設定するコードが書き込まれるため、Visual Studio 2010を通常どおり使用する場合、遅延読み込みは有効となる。本来のデフォルト値(False)と、実質的なデフォルト値(True)が異なることに注意したい。

 なお、このプロパティは次の図3のように、ADO.NET Entity Data Modelツールのエンティティ・コンテナのプロパティで設定できる。


図3 [遅延読み込みが有効です]プロパティがデフォルトでTrueに設定されている

 遅延読み込みは便利な機能であるが、遅延読み込みが発生するたびにデータベースへのクエリが発行されるため、ループ内で遅延読み込み処理を行うとクエリ回数が増大することに注意が必要である。これを「N+1検索問題」(最初の1回+ループでの遅延読み込みN回のクエリが発生)と呼ぶ。

 同じケースをEager-loadingで処理した場合、最初に関係するエンティティをまとめて読み込むため、クエリは1回だけで済むことになる。しかしながら、Eager-loadingを行うと、クエリ結果が大きくなり、メモリ使用量やパフォーマンスに問題が起きる場合もあるため、状況に応じた使い分けが必要である。

EntityClientデータ・プロバイダによるクエリ

 EntityClientデータ・プロバイダはEntity Frameworkにおいて、データベースにアクセスするための基本的な機能を提供するコンポーネントである。前述のObject Servicesも、実際のリクエストは、このEntityClientデータ・プロバイダを用いて行っている。

 EntityClientデータ・プロバイダはADO.NETデータ・プロバイダと同じような表1のような構成となっているため、ADO.NETデータ・プロバイダと同様の流れで呼び出すことができる。ただし、EntityClientデータ・プロバイダは結果をオブジェクトとして返すことはできず、結果はプリミティブなデータの集合となる。

EntityClient
データ・プロバイダ
ADO.NET
データ・プロバイダ
データ接続クラス EntityConnection DbConnection
実行するコマンドクラス EntityCommand DbCommand
データ読み込みクラス EntityDataReader DbDataReader
表1 EntityClientデータ・プロバイダとADO.NETデータ・プロバイダとの対応

 EntityClientデータ・プロバイダを直接呼び出す際は、Entity SQLを用いて呼び出しを行うことになる。EntityClientデータ・プロバイダの呼び出し方法については、前述の「特集:Visual Studio 2008 SP1新機能解説(2).NETの新データアクセス・テクノロジ『ADO.NET Entity Framework』」を参照いただきたい。


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

本日 月間