ScottGu's blog translated by Chica @ Wankuma

LINQ to SQL (パート 3 - データベースの検索)

  

先月からLINQ to SQLをカバーしたブログの投稿を始めました。 LINQ to SQL とは、 .NET Framework "Orcas"リリースで出荷されるO/RM (オブジェクトリレーショナルマッピング)の実装のことです。これにより、.NETクラスを使用してリレーショナルデータベースをモデル化することができます。そして、LINQを使用してデータベースの検索、またそこからデータの更新・挿入・削除も行うことができます。

以下はLINQ to SQL シリーズの最初の2つです。:

今日の投稿では、パート2で作成したデータモデルの使用方法についてより詳細に入り込み、ASP.NETプロジェクト内でデータを検索するためにどのようにそれを使用するかについてご説明したいと思います。 .

LINQ to SQL使用したNorthwindデータベースのモデル化

このシリーズのパート 2 では、VS2008にビルドインされているLINQ to SQLデザイナを使用したLINQ to SQLクラスモデルの作成方法をカバーしました。以下はNorthwindサンプルデータベースに対して作成したクラスモデルです。

Productsのデータ取得

上記で一度データモデルクラスを定義すると、データベースから簡単にデータの検索や取得を行うことができます。これは、上記でLINQ to SQLデザイナを使用して作成したNorthwindDataContext クラスに対してLINQシンタックスクエリを書くことで実現することができます。

例えば、一連のProductオブジェクトを取得して、個々のオブジェクトにアクセスするには以下のようにコードを書きます。:

上記のクエリで、私はLINQシンタックスクエリに"where"節を使用して、あるカテゴリ内の製品だけを返すようにしています。 フィルタとしてProductテーブルのCategoryIDを使用しています。

上記のLINQ to SQLのいいところですが、データ検索の方法にかなりの柔軟性が持てることと、 データベースに対してよりリッチでより自然なクエリを実行するために、LINQ to SQLのデータクラスをモデル化した際に設定したアソシエーション(関連性)を利用することができることです。 例えば、LINQクエリを次の様に書くことで、製品のCategoryIDの代わりにCategoryNameでフィルタをかけるようクエリを修正することができます。 :

上記で、私がどのように各Productオブジェクト上の"Category"プロパティを使用して、そのProductが属しているCategoryのCategoryNameでフィルタをかけているかをご確認ください。このプロパティは、データベースで複数対1の関係性をそれぞれ持たせるようにCategoryとProductクラスをモデル化したため、 自動的にLINQ to SQLによって作成されたものです。

クエリ内でデータモデルの関連関係を使用する簡単な別の例として、 以下のような、5つ以上注文されている製品だけを取得するLINQクエリを書くことが出来ます。 :

LINQ to SQLが各Productクラス(1対複数の関係をLINQ to SQLデザイナでモデル化したため)上に作成してくれた"OrderDetails"コレクションをどのように使用しているかを上記でご確認ください。

デバッガでLINQ to SQLクエリを視覚化

LINQ to SQLのようなオブジェクト リレーショナル マッパーは、オブジェクトモデルに対して検索や更新を実行した時に、自動的に適切なSQLコードを作成・実行してくれます。

ORMが初めての開発者が持つ最大の心配事の1つとして、"でもどのSQLコードが実際に実行されているんですか?"という質問があります。 LINQ to SQLの非常にいい部分の1つですが、デバッガでアプリケーションを動作させている時に どのSQLコードが実際に実行されているかを非常に簡単に確認することができます。

Visual Studio 2008のベータ2で始めた場合、新しいLINQ to SQLのビジュアライザ プラグインを使って、どんなLINQ to SQLクエリ式でも簡単に確認(またテスト)することが出来ます。 単純にブレークポイントを設定して、LINQ to SQLクエリ上にマウスオーバーし、 拡大鏡をクリックしてデバッガで式ビジュアライザを引き出します。 :

これはダイアログを出して正確なSQLを表示します。LINQ to SQLはProductオブジェクトを取得するクエリを実行する時にそれを使用します。:

このダイアログ内で"Execute"ボタンを押すと、 デバッガ内で直接SQLの評価ができ、データベースから返ってきたデータ結果だけを見ることができます。 :

明らかにこれでLINQ to SQLがどのSQLクエリロジックを行ってくれているかを非常に簡単に正確に見ることができます。またLINQ to SQLが実行する単純なSQLを変更したい場合オーバーライドすることもできます。 - しかし、98%のシナリオでLINQ to SQLが実行するSQLコードが非常に非常にいいと思われると思います。

ASP.NETコントロールにLINQ to SQLクエリをデータバインディング

LINQクエリはIEnumerableインターフェイスを実装した結果を返します。 - またASP.NETサーバコントロールがオブジェクトをデータバインドするためのインターフェイスでもあります。 これはLINQや、LINQ to SQL、またLINQ to XMLクエリのどの結果もASP.NETコントロールにデータバインドすることができるということを意味します。

例えば、以下のようにして<asp:gridview>コントロールを.aspxページに宣言します。

そうすると前に書いたLINQ to SQLクエリの結果を以下の様にGridViewにデータバインドすることができます。 :

そして、これが以下のようなページを生成します。

クエリ結果の形成

現在、製品のクエリを評価している場合は、デフォルトではProductのエンティティクラスを紐付けるのに必要なカラムデータをすべて取得します。

例えば、製品データを取得するこのクエリ:

結果として、このデータの全てが返されました。:

 

各製品に対するデータのサブセットだけを返してほしいことがよくあります。新しい データ形成機能 を使う事ができるので、次のようにLINQ to SQLクエリを修正すれば、LINQや新しいC#およびVBのコンパイラはデータのサブセットだけが欲しいことを提示するサポートを行います。 :

これでデータベースからこのデータのサブセットだけが返される様になります。(デバッグビジュアライザで確認したように):

LINQ to SQLですごい事は、データ形成時にデータモデルクラスの関連性を精一杯利用できることです。 これにより、非常に有用な(そして非常に効率的な)データクエリが表現できるようになります。例えば、以下のクエリでは、ProductエンティティからIDおよびNameとProductに対する注文総数を取得し、その後各Product注文の総利益値を合計します。 :

上記の"Revenue"プロパティの右にある式はLINQが提供した "Sum" 拡張メソッド を使用した例です。引数として各製品の注文項目の値を返す ラムダ式 を取ります。

LINQ to SQLは賢くて、評価の時(デバッグビジュアライザを通して見た様に)に上記のLINQ式を以下のSQLに変換することができます。 :

上記のSQLはSQLサーバ内で全てのNumOrdersとRevenue値の計算を完了するので、その結果データベースから以下のデータのみが取得されます。(非常に早くします。) :

そして、かっこいいUIを生成するためのGridViewコントロールに対して、結果のシーケンスをデータバインドすることができます。 :

ちなみに、これらのタイプのLINQ形成クエリを書いている時、VS2008で全てのインテリセンスを取得することができます。 :

上記の例では、 匿名タイプを宣言しています。それは オブジェクトの初期化 を使って結果構造を形成・定義します。 非常にすごい事は、VS2008がこれらの匿名の結果シーケンスを処理している時も、全てのインテリセンスや、コンパイルチェック、およびリファクタリングのサポートを提供していることです。

 

クエリ結果のページング

Webシナリオでよくあるニーズの1つですが、効率的なデータページングUIがビルドできることです。 LINQは2つの拡張メソッド(Skip()とTake() メソッド)に対してビルドインでサポートしているのでこれを簡単かつ効率的に行えます。

以下のようにSkip() とTake()メソッドを使い、10個の製品オブジェクト(最初の製品の行から引数で特定した行まで)だけ返してほしいことを示すことができます。

上記で、私がSkip()とTake()演算子を最初の製品検索で追加しないで、後で(GridViewデータソースにバインディングする時)追加していることを確認してください。" これだとクエリが最初にデータベースから全てのデータを取ってきて、中間層(これはよくない)でページングを行うのでは?"とよく聞かれますが、違います。理由は、LINQが遅延実行モデルを使用しているからです。- そのクエリの結果を制御するまで実際には実行されないことを意味しています。

この遅延実行モデルの利点の1つとして、これにより複数のコード文に渡ってクエリを上手く作成することができます。(これはコードの読みやすさをを向上します) またその他のクエリからクエリを作成することもできます。 - そしてこれが非常に柔軟なクエリの作成と再利用のシナリオを可能にします。

上記で定義したBindProduct()メソッドがあるので、ページ内に以下のようなコードを書いてクエリストリングから開始インデックスを取得すれば、グリッドビューで製品がページ指定して表示されるようになります。 :

これで、5つ以上の注文がある製品だけを一覧表示するようにフィルタがかかった、動的に計算された製品データを表示する、製品ページが表示されます。このページはクエリストリング引数によりページングすることができます。

注意:SQL 2005を使用している場合、LINQ to SQLはROW_NUMBER() SQL 機能を使用してデータベースで全てのページングロジックを実行します。これにより、上記コードを実行した際、私たちが現在のページビュー上で必要としている10行のデータのみがデータベースから返されます。

これが大きなデータシーケンスに渡るページングを効率的かつ簡単にします。

まとめ

上記チュートリアルでLINQ to SQLが提供するかっこいいデータクエリの方法を上手くまとめてご紹介できていれば幸いです。LINQ文やVS2008のC#やVBコンパイラがサポートする新しい言語シンタックスについての詳細は、以前の投稿分をお読みください。:

このLINQ to SQLシリーズの次の投稿では、データモデルクラスにどのようにしてきれいに評価ロジックを追加するのかをカバーし、そしてデータを更新、挿入、削除する度にビジネスロジックをカプセル化するにはそれをどのように使うのかについてご紹介したいと思います。そのあと、より上級の遅延・先行ローディングクエリシナリオや、ASP.NETコントロールの宣言型データバインディングをサポートする新しい<asp:LINQDataSource>コントロールの使い方、またオプティミスティック同時実行制御エラー解決などについてカバーしたいと思います。

Hope this helps,

Scott

ScottGu's blog translated by Chica @ Wankuma 

※本翻訳に関しまして、Scottさんにはご了承頂いております。