特集:Visual Studio 2008 SP1新機能解説(2)

.NETの新データアクセス・テクノロジ
「ADO.NET Entity Framework」

WINGSプロジェクト ナオキ/山田 祥寛
2008/11/28
Page1 Page2 Page3 Page4

■Query Builderメソッドを利用する(Object Services)

 Object Servicesは、EDMに対してクエリを発行するためのコンポーネントだ。Object Servicesでは、Entity SQLのほかに、Query BuilderメソッドやLINQ to Entitiesを利用してクエリを発行できる。

 ここでは、最初にQuery Builderメソッドを利用してObjectQueryオブジェクトに対してクエリを発行する方法を解説する。ObjectQueryオブジェクトとは、Object Servicesの中でクエリを管理し、また、その結果(エンティティのコレクション)を保持するためのオブジェクトである。

 なお、Query BuilderメソッドはEntity SQLのステートメントを構築するためのメソッドの総称で、パラメータの部分だけを文字列として渡すことができるのが特徴だ。パラメータ部分の文字列には「ObjectQuery<T>型の名前.条件式」の形式で記述するのだが、既定の設定ではObjectQuery<T>型の名前は「it」と設定されている。

EDMオブジェクト objCtx = new EDMオブジェクト();

ObjectQuery<エンティティ型> objQuery =
            objCtx.エンティティ型.Where("it.条件式");
Dim objCtx As New EDMオブジェクト()

Dim objQuery As ObjectQuery(Of エンティティ型) = _
            objCtx.エンティティ型.Where("it.条件式")
WHERE句を使った基本的なQueryBuilderメソッド構文(上:C#版、下:VB版)

 Query BuilderメソッドがEntity Client+Entity SQLと異なる点は、EDMで定義した型による戻り値を取得できることだ。これにより、戻り値をそのままコントロールにバインドすることも可能になる。

 実際にQuery Builderメソッドを利用した例は、以下のとおり。

using System.Data.Objects; // この行はリストの先頭部分に追加
using pubsModel;           // この行はリストの先頭部分に追加

protected void Page_Load(object sender, EventArgs e)
{
  // pubsEntitiesオブジェクトの生成
  using (pubsEntities objCtx = new pubsEntities())
  {
    int royalty = 12;

    // Query Builderメソッドで利用するために
    // ObjectQuery<titles>名を"titles"に設定
    objCtx.titles.Name = "titles";

    ObjectQuery<titles> objQuery
                = objCtx.titles.Where(
                        "titles.royalty < @royalty",
                        new ObjectParameter("royalty", royalty));

    // GridView1にデータをバインディングする
    GridView1.DataSource = objQuery;
    GridView1.DataBind();
  }
}
Imports System.Data.Objects ' この行はリストの先頭部分に追加
Imports pubsModel           ' この行はリストの先頭部分に追加

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

  ' pubsEntitiesオブジェクトの生成
  Using objCtx As New pubsEntities()

    Dim royalty As Integer = 12

    ' Query Builderメソッドで利用するために
    ' ObjectQuery<titles>名を"titles"に設定
    objCtx.titles.Name = "titles"

    Dim objQuery As ObjectQuery(Of titles) = _
                      objCtx.titles.Where(_
                        "titles.royalty < @royalty", _
                        New ObjectParameter("royalty", royalty))

    ' GridView1にデータをバインディングする
    GridView1.DataSource = objQuery
    GridView1.DataBind()
  End Using
End Sub
リスト2  Query Builderメソッドを利用してPubsEntitiesオブジェクトからroyaltyプロパティが12以下のレコードを取得し、GridView1にバインディングするコード(上:C#版、下:VB版)

 この例では、ObjectQuery<T>型の名前を、Nameプロパティを利用してtitlesと設定している。ここで登場するWhereメソッドは、クエリ結果に条件を指定するEntity SQLのWhereステートメントと意味的に等価で、第1パラメータで指定されたEntity SQLを実行するものだ。第2パラメータにはクエリに渡すエスケープ済みのパラメータ値を指定することができる。

 Entity SQL同様、Query BuilderメソッドもクエリとしてINSERT/UPDATE/DELETE句はサポートしていないが、SaveChangesメソッドを使用することで、追加/更新/削除の処理を行える。

 SaveChangesメソッドを利用した例は、以下のようになる。この例はPubsEntitiesオブジェクトに対してtitlesオブジェクトの要素を追加、要素の1項目を更新、追加した要素を削除するコードだ。

using System.Data.Objects; // この行はリストの先頭部分に追加
using pubsModel;           // この行はリストの先頭部分に追加

// 追加
// PubsEntitiesオブジェクトの生成
using(pubsEntities objCtx = new pubsEntities())
{
  // titlesエンティティ・オブジェクトの生成
  titles titlesEntity = new titles();
  DateTime dt = DateTime.Parse("2008/09/17");

  // titlesエンティティ・オブジェクトに追加するレコードの値を入力
  titlesEntity.pubdate = dt;
  titlesEntity.royalty = 8;
  titlesEntity.title = "Tamaki";
  titlesEntity.title_id = "NO311";
  titlesEntity.type = "Family";

  // 入力された要素を新しいオブジェクトとして
  // titlesエンティティに追加
  objCtx.AddObject("titles", titlesEntity);

  // 更新をストアに反映
  objCtx.SaveChanges();
}

// 更新
// PubsEntitiesオブジェクトの生成
using (pubsEntities objCtx = new pubsEntities())
{
  // titlesエンティティ・オブジェクトの生成
  titles titlesEntity = new titles();

  // royaltyプロパティが8である最初のエンティティ(レコード)を更新
  titlesEntity = objCtx.titles.Where("it.royalty = 8").First();

  // 返された要素のtitleプロパティの値を設定
  titlesEntity.title = "Tamata";

  // 更新をストアに反映
  objCtx.SaveChanges();
}

// 削除
// PubsEntitiesオブジェクトの生成
using (pubsEntities objCtx = new pubsEntities())
{
  // titlesエンティティ・オブジェクトの生成
  titles titlesEntity = new titles();

  // royaltyプロパティが8である最初のエンティティ(レコード)を削除
  titlesEntity = objCtx.titles.Where("it.royalty = 8").First();

  // 返された要素のオブジェクトを削除
  objCtx.DeleteObject(titlesEntity);

  // 更新をストアに反映
  objCtx.SaveChanges();
}

Imports System.Data.Objects ' この行はリストの先頭部分に追加
Imports pubsModel           ' この行はリストの先頭部分に追加

' 追加
' PubsEntitiesオブジェクトの生成
Using objCtx As New pubsEntities()

  ' titlesエンティティ・オブジェクトの生成
  Dim titlesEntity As New titles()
  Dim dt As DateTime = DateTime.Parse("2008/09/17")

  ' titlesエンティティ・オブジェクトに追加するレコードの値を入力
  titlesEntity.pubdate = dt
  titlesEntity.royalty = 8
  titlesEntity.title = "Tamaki"
  titlesEntity.title_id = "NO311"
  titlesEntity.type = "Family"

  ' 入力された要素を新しいオブジェクトとしてtitlesエンティティに追加
  objCtx.AddObject("titles", titlesEntity)

  ' 更新をストアに反映
  objCtx.SaveChanges()
End Using

' 更新
' PubsEntitiesオブジェクトの生成
Using objCtx As New pubsEntities()

  ' titlesエンティティ・オブジェクトの生成
  Dim titlesEntity As New titles()

  '  royaltyプロパティが8である最初のエンティティ(レコード)を更新
  titlesEntity = objCtx.titles.Where("it.royalty = 8").First()

  ' 返された要素のtitleプロパティの値を設定
  titlesEntity.title = "Tamata"

  ' 更新をストアに反映
  objCtx.SaveChanges()

End Using

' 削除
' PubsEntitiesオブジェクトの生成
Using objCtx As New pubsEntities()

  ' titlesエンティティ・オブジェクトの生成
  Dim titlesEntity As New titles()

  ' royaltyプロパティが8である最初のエンティティ(レコード)を削除
  titlesEntity = objCtx.titles.Where("it.royalty = 8").First()

  ' 返された要素のオブジェクトを削除
  objCtx.DeleteObject(titlesEntity)

  ' 更新をストアに反映
  objCtx.SaveChanges()

End Using
リスト3 SaveChangesメソッドを利用した、追加/更新/削除の基本的なコード(上:C#版、下:VB版)

 追加を行うには、titlesエンティティを組み立て、AddObjectメソッドでPubsEntitiesオブジェクトに追加するだけだ。追加された要素をSaveChangesメソッドでストアに反映させているが、その前にAddObjectメソッドを呼び出す必要がある。

 更新時には、Query Builderメソッドの1つであるWhereメソッドを使い、更新対象となるtitlesエンティティのコレクションを取得する。本来であれば、コレクション全体に対して処理を行うところであるが、ここでは取りあえずFirstメソッドで取り出した最初の要素に対してのみ処理を行っておくことにしよう。更新を行うにはエンティティ上の目的のプロパティを変更したうえで、SaveChangesメソッドを呼び出すだけでよい。

 削除も同様だ。WhereメソッドとFirstメソッドで対象となるエンティティを取り出した後、DeleteObjectメソッドを呼び出せばよい。削除結果は追加/更新と同様、SaveChangesメソッドの呼び出しによって、データベースに反映される。

 このように、Entity Frameworkではクエリとしては追加/更新/削除を行えなくとも、機能としては追加/更新/削除をサポートしていることがお分かりいただけたかと思う。

■LINQ to Entitiesを利用する(Object Services)

 Object ServicesはLINQと組み合わせて利用することもできる。LINQのクエリ対象はEDMなので、使用するのは「LINQ to Entities」である。

 LINQ to Entitiesを利用する場合、前述した2種類の方法に比べてアドホック・クエリの利用に向いているとはいえない。LINQの特徴は厳密な型指定にあるためだ(もちろん、EDMからデータ・コントロールに直接エンティティをバインドできる)。

 アドホック・クエリが必要な場合でのみEntity SQLなどを利用し、それ以外はできる限りLINQ to Entitiesでコーディングを行うと開発生産性の向上が望めるだろう。

 以下のコードは、実際にLINQ to Entitiesを利用した例だ。

using pubsModel; // この行はリストの先頭行に追加

protected void Page_Load(object sender, EventArgs e)
{
  // pubsEntitiesオブジェクトの生成
  var pubsEntity = new pubsEntities();

  // LINQによるクエリの定義
  var pubs = from p in pubsEntity.titles
             where p.royalty < 12
             select p;

  // GridView1にデータをバインディングする
  GridView1.DataSource = pubs;
  GridView1.DataBind();
}
Imports pubsModel ' この行はリストの先頭部分に追加

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

  ' pubsEntitiesオブジェクトの生成
  Dim pubsEntity As New pubsEntities()

  ' LINQによるクエリの定義
  Dim pubs = From p In pubsEntity.titles _
             Where p.royalty < 12 _
             Select p

  ' GridView1にデータをバインディングする
  GridView1.DataSource = pubs
  GridView1.DataBind()

End Sub
リスト4  LINQ to Entitiesを利用してPubsEntitiesオブジェクトからroyaltyプロパティが12以下のレコードを取得し、GridViewコントロールにバインディングするコード(上:C#版、下:VB版)

 リスト中のクエリ式については特筆すべき点はない。EDMに対してLINQにより問い合わせを行い、データ・コントロールに、エンティティをバインディングしているだけだ。

まとめ

 ADO.NET Entity Frameworkは概念レベルでの開発を可能にし、LINQ同様、従来のデータ・モデリング手法やアプリケーション開発手法をドラスティックに変更する可能性を秘めたフレームワークである。しかし、登場したばかりの技術ということもあり、データ・プロバイダが充実していない(デフォルトではSQL Serverにのみ対応)、DMLクエリがサポートされていない、また、集合データに対しての一括更新クエリが実行できないなどの制限もある。

 特に、最後の問題は更新系アプリケーションでは致命的かもしれないが、参照系アプリケーションでは強力なフレームワークとなることが本稿の内容からもお分かりいただけたかと思う。

 ただし、Entity Frameworkについては、以前からさまざまな議論がネット上で行われている。否定的な意見のほとんどは「開発に必要な機能を十分に満たしていない」というものだ。具体的には、以下のような論点が挙げられている。

  • データをすべて読み込むのではなく、必要なときに必要なデータを読み込むためのオブジェクト(=レイジー・ロード)がないため、それを記述するためのコードが必要になる。
  • EDMという単一のXMLファイルに対して複数の開発者が同時に作業を行うことになるので、特に単一のエンティティ型に対して複数の論理テーブルをマッピングした場合には、変更の衝突が発生しやすい。

 事実、マイクロソフトのEntity Framework開発チームも、バージョン1.0ではこれらのシナリオを網羅していないことを認めている。しかし、Entity Frameworkのロードマップにおいては、開発者の要望を取り入れたバージョンをリリース予定であることをブログで回答している。

 また、現時点ではEntity FrameworkよりもまだまだLINQ to SQLの方が多く利用されているが、今後、Entity Framework開発チームはEntity Frameworkに注力していくことも明言している

 このように、Entity Framework 1.0はいくつか課題も抱えているが、冒頭で述べたように、それら課題を上回るさまざまなアドバンテージを提供している。今後、.NET Frameworkで主流になっていくであろうデータアクセス・テクノロジに、ぜひ積極的に触れていただきたい。End of Article


 INDEX
  Visual Studio 2008 SP1新機能解説(1)
  DBアプリをコーディングレスで構築する「ASP.NET Dynamic Data」
    1.Dynamic Dataアプリケーションの基本
    2.データ・モデルの作成/Global.asaxの編集
    3.メタデータ編集によるDynamic Dataアプリケーションのカスタマイズ
    4.自作ユーザー・コントロールの利用/ページ・テンプレートのカスタマイズ
 
  Visual Studio 2008 SP1新機能解説(2)
  .NETの新データアクセス・テクノロジ「ADO.NET Entity Framework」
    1.ADO.NET Entity Framework概要
    2.Entity Data Model(EDM)の作成と利用
    3.EDMに対するクエリの利用方法(Entity SQL)
  4.EDMに対するクエリの利用方法(Object Services/LINQ to Entities)
 
  Visual Studio 2008 SP1新機能解説(3)
  RESTスタイルのWebサービスを手軽に公開する「ADO.NET Data Services」
    1.ADO.NET Data Services概要/対応するクライアント
    2.サンプル − ADO.NET Data Servicesの公開
    3.ASP.NET AJAX環境のデータを取得/追加する


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

本日 月間