特集
» 2008年03月25日 00時00分 公開

特集:C#プログラマーのためのLINQ超入門(前編):LINQ(リンク)の基礎知識 (3/4)

[遠藤孝信,デジタルアドバンテージ]

クエリの実行を支えるさまざまなLINQプロバイダ

 このように、LINQはデータベースだけでなく、同じ構文でコレクションや配列にも使えます。これら以外にも、XMLデータやADO.NETのデータセット(DataSet)などに対しても利用できます。LINQの構文は、どれに対してもすべて同じです。

 想像するに「C#からSQL文を発行するときにはそれを文字列として記述しなければならず不便。思い切って言語に取り込んでみては? そうだ、そうすれば同じ記述でコレクションやXMLにも使えて便利だぞ!」といった感じでしょうか。

 しかしもちろん、実行時の処理は、例えばデータベースとコレクションとではまったく異なります。データベースの場合には、クエリからSQL文を生成してデータベース・サーバへ送信する仕組みが必要になりますから。

 LINQでは、問い合わせ先であるデータソース(「from n in XXX」のXXXの部分。これについてはすぐ次で解説しています)の型に基づいて、クエリがどのようにコンパイルされ、実行されるのかが決まる仕組みになっています。端的にいえば、最終的に問い合わせ処理の実行を担当するクラス(通常はクラス群)がクエリ対象の種類によって異なるというわけです。このようなデータソースの種類ごとに存在する一連のクラス群は「LINQプロバイダ」と呼ばれます。

 ADO.NETではデータ・プロバイダをSQL Server用やOracle用に切り替えるだけで、同じコードで異なるデータベース・システムにアクセスできる仕組みになっていました。同様にLINQでは、LINQプロバイダを切り替えることにより、同じクエリ構文でさまざまな対象を検索できるようになっています。上述したように、使用されるLINQプロバイダはクエリ対象によって決まります。

 .NET Framework 3.5では、標準で次の図に示すようなLINQプロバイダが用意されています。

.NET Framework 3.5で用意されているLINQプロバイダ

 例えば、先ほどのリスト2やリスト3のように、LINQによりデータベースを検索するときには「LINQ to SQL」の機能を利用していることになります。また、リスト5の配列への問い合わせは「LINQ to Object」の機能です。

 また、「LINQ to DataSet」はADO.NETのデータセットに対して、「LINQ to XML」はXMLデータに対してLINQを実行可能にするためのLINQプロバイダです。

 データセットはデータベースから取得した結果を格納するためのADO.NETの仕組みですが、そこに格納されたデータの検索に関してはSelectメソッド*1ぐらいしか手段が用意されていませんでした。またXMLに関しては、特定の要素を取得するのにXPathやXQueryといった機能を使うのが標準的でした。LINQをマスターしていれば、これらのデータも同じLINQの構文で統一的に扱えることになります。

*1 データセット内に含まれるデータテーブルのSelectメソッド。


 もちろん、ADO.NETのデータ・プロバイダと同様に、独自のLINQプロバイダ(LINQ to XXX)を作成することも可能です。そのためのフレームワークはクラス・ライブラリに用意されています。すでに、「LINQ to Amazon」「LINQ to Excel」「LINQ to Flickr」「LINQ to Google」といったLINQプロバイダが作成されているようです*2

*2Links to LINQ」にまとめページがありました。


LINQで使用するキーワード

 LINQによるクエリの書き方をもう少し詳しく見ていきましょう。

from n in data
where n.ShipCountry == "Norway"
select n;

リスト6 リスト5から抜粋したLINQによるクエリ

 LINQのクエリは基本的に「from句」で始まり、「select句」で終わります。ここで登場している「from句(from 〜 in 〜)」「where句」「select句」は、クエリ用のC#の新しいキーワードです。

 C#では、このようなクエリ用のキーワードとして以下のものが用意されています。C#ではこれらのキーワードはすべて小文字で記述する必要があります。

キーワード 機能
from データソースおよび範囲変数を指定する
where フィルタ条件を指定する
select クエリ結果の要素の形式を指定する
group 指定したキーによりクエリ結果をグループ化する
orderby クエリ結果をソートする
join 2つのデータソースを結合する
let 新しい範囲変数を作成する
into select、join、groupの結果を一時変数に格納する
表1 LINQで使用するクエリ用のキーワード一覧
各キーワードの正確な形式や詳細はそれぞれのリンクからオンライン・リファレンスを参照してほしい。

 ここでは、リスト6で使っているfrom、where、selectについて説明します。

■from句

 まず、from句は、

from <範囲変数> in <データソース>

の形式で記述します。

 「データソース」にはLINQによる問い合わせの対象を指定します。ここには、IEnumerable<T>インターフェイス(あるいはIEnumerableインターフェイス、あるいはそれらの派生インターフェイス)を実装したオブジェクトが指定できます。このインターフェイスを実装しているオブジェクトは、列挙可能なコレクションです。これは簡単にいってしまえば、foreachループが使えるオブジェクトです。

 例えば、List<int>クラスはint型オブジェクトのリストですが、List<T>クラスはIEnumerable<T>を実装していますので、LINQが使えます。また、配列やArrayListクラスはIEnumerableインターフェイスを実装していますのでLINQが使えます。

 「範囲変数(range variable)」では、データソース内の各要素を入れる変数を指定します。データソースがIEnumerable<T>インターフェイスを実装していれば、範囲変数の型は自明ですので(データソースがList<int>型なら範囲変数はint型)、範囲変数の型指定は省略できます*3

*3データソースにArrayListオブジェクトを指定した場合、範囲変数には要素の型を指定する必要があります。


■where句とselect句

 where句ではフィルタとなる条件式を記述します。条件式は真(true)あるいは偽(false)となる式です。複数の条件を記述する場合には「&&」や「||」を使います。C#の条件式と似ていますが、実はwhere句に記述する条件式はC#の条件式そのものです。ここで指定した式が具体的にどのような形で利用されるかについては、後編で説明します。

 一方、select句では、クエリの戻り値となる形式を指定します。ここでの型によってクエリ結果の型が決まります。リスト6では「select n」としていますので、範囲変数nをそのままの形で出力します。従って、範囲変数がOrders型であれば、クエリ結果はIEnumerable<Orders>型となります。リスト6で、もし「select n.ShipCountry」とした場合には、クエリ結果はIEnumerable<string>型となります。

 C# 3.0の新しい機能である「匿名型」を使えば、複数の項目を一時的に集めて1つのオブジェクトとしてまとめることもできますが、これについても後編で解説します。ちなみにこの場合には、クエリ結果を代入する変数はvarキーワードを使って宣言する必要があります。

■LINQの標準クエリ演算子

 where句やselect句といったLINQのキーワードは、実はコンパイラによりメソッドの呼び出しに変換されます。例えば、where句は「Whereメソッド」、select句は「Selectメソッド」の呼び出しになります。

 これらのメソッドは、System.Linq名前空間のEnumerableクラスで定義されています(LINQ to Objectの場合)。このクラスには約50個の静的メソッドが定義されていますが、これらのメソッドは「標準クエリ演算子(Standard Query Operator)」と呼ばれ、LINQのクエリを記述するために用意されています。

 標準クエリ演算子のメソッドすべてに、C#のキーワードが対応しているわけではありませんので(表1に挙げたキーワードがC#のLINQ用のキーワードのすべてです)、実際には表1のキーワードと標準クエリ演算子のメソッドを混在させてクエリを記述することになります。

 あるいは表1のキーワードをまったく使用しないで、すべてメソッド呼び出しでクエリを記述することもできます。後編ではクエリがどのようにメソッドに置き換えられるかについて解説しています。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。