初めての LINQ クエリの作成 (Visual Basic)
更新 : 2007 年 11 月
クエリとは、データ ソースからデータを取得するための式です。クエリは、専用のクエリ言語で表されます。これまでに、リレーショナル データベース用の SQL や XML 用の XQuery など、データ ソースの種類に合わせてさまざまな言語が開発されてきました。このため、アプリケーション開発者は、サポートするデータ ソースの種類やデータ形式ごとに新しいクエリ言語を習得する必要が生じています。
統合言語クエリ (LINQ: Language-Integrated Query) は、さまざまな種類のデータ ソースやデータ形式のデータを操作するための一貫したモデルを提供することにより、この負担を軽減します。LINQ クエリでは、操作の対象は常にオブジェクトになります。共通の基本的なコーディング パターンを使用することで、XML ドキュメント、SQL データベース、ADO.NET のデータセットとエンティティ、.NET Framework コレクション、および LINQ プロバイダを利用できる他の任意のソースまたは形式のデータを照会したり変換したりできます。ここでは、基本的な LINQ クエリの作成と使用に関する 3 つの段階について説明します。
クエリ操作の 3 つの段階
LINQ クエリ操作は、次の 3 つの手順で構成されます。
データ ソースを取得します。
クエリを作成します。
クエリを実行します。
LINQ では、クエリの実行とクエリの作成は別々の操作になります。クエリを作成するだけでは、データは取得されません。この点については、後で詳しく説明します。
クエリ操作を構成する 3 つの部分を次の例に示します。この例では、デモンストレーションのために、わかりやすいデータ ソースとして整数の配列を使用しています。ただし、他のデータ ソースを使用する場合にも同じ概念が当てはまります。
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers _
Where num Mod 2 = 0 _
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
出力
0 2 4 6
データ ソース
前の例では、データ ソースが配列であるため、暗黙的にジェネリック IEnumerable<T> インターフェイスがサポートされます。LINQ クエリのデータ ソースとして配列を使用できるのは、この動作によります。IEnumerable(Of T) をサポートする型や、ジェネリック IQueryable<T> などの派生インターフェイスは、クエリ可能型と呼ばれます。
配列は暗黙的にクエリ可能型であるため、変更や特別な処理を行わなくても、LINQ データ ソースとして使用できます。ジェネリック List<T>、Dictionary<TKey, TValue>、.NET Framework クラス ライブラリの他のクラスなど、IEnumerable(Of T) をサポートする任意のコレクション型についても同じことが言えます。
ソース データが IEnumerable(Of T) を実装していない場合は、そのデータ ソースに対応した標準クエリ演算子の機能を実装する LINQ プロバイダが必要です。たとえば、LINQ to XML は、次の例に示すように、クエリ可能な XElement 型に XML ドキュメントを読み込む処理を行います。標準クエリ演算子の詳細については、「標準クエリ演算子の概要」を参照してください。
' Create a data source from an XML document.
Dim contacts As XElement = XElement.Load("c:\myContactList.xml")
LINQ to SQL では、まず、デザイン時に手動で、またはオブジェクト リレーショナル デザイナ (O/R デザイナ) を使用して、オブジェクト リレーショナル マッピングを作成します。オブジェクトに対するクエリを記述すると、実行時には、LINQ to SQL によってデータベースとの通信が処理されます。次の例では、customers はデータベースのテーブルを表し、Table<TEntity> はジェネリック IQueryable<T> をサポートします。
' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)
それぞれの種類のデータ ソースを作成する方法の詳細については、対応する LINQ プロバイダのドキュメントを参照してください。これらのプロバイダの一覧については、「統合言語クエリ (LINQ: Language-Integrated Query)」を参照してください。基本的な規則は単純です。LINQ データ ソースは、ジェネリック IEnumerable<T> インターフェイス、またはこれを継承するインターフェイスをサポートする任意のオブジェクトです。
メモ : |
---|
非ジェネリック IEnumerable インターフェイスをサポートする ArrayList などの型も、LINQ データ ソースとして使用できます。ArrayList の使用例については、「方法 : LINQ を使用して ArrayList を照会する」を参照してください。 |
クエリ
クエリでは、データ ソースからどのような情報を取得するかを指定します。情報が返される前に、その情報を並べ替え、グループ化し、構造化する方法を指定するオプションもあります。クエリの作成を可能にするために、Visual Basic 言語に新しいクエリ構文が組み込まれています。
次の例のクエリを実行すると、整数の配列 numbers からすべての偶数が返されます。
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers _
Where num Mod 2 = 0 _
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
クエリ式には、From、Where、および Select の 3 つの句が含まれています。クエリ式の各句の機能と目的については、「基本的なクエリ操作 (Visual Basic)」で説明しています。詳細については、「クエリ (Visual Basic)」を参照してください。LINQ では、クエリ定義が変数に格納され、後で実行されることがよくあります。前の例の evensQuery のようなクエリ変数は、クエリ可能型でなければなりません。evensQuery の型は、ローカル型の推論を使用して、コンパイラにより IEnumerable(Of Integer) が割り当てられます。
クエリ変数自体は何も処理を行わず、データを返さないことに注意してください。これは、クエリ定義を格納するだけです。前の例では、クエリを実行するのは For Each ループです。
クエリの実行
クエリの実行は、クエリの作成とは別の操作です。クエリの作成ではクエリを定義しますが、実行は別の機構によって発生します。クエリは、定義後にすぐに実行 (即時実行) することも、定義を保存して後で実行 (遅延実行) することもできます。
遅延実行
一般的な LINQ クエリでは、前の例の evensQuery ようなものが定義されます。つまり、クエリを作成しても、すぐには実行されません。代わりに、クエリ変数 evensQuery にクエリ定義が格納されます。クエリは後で実行され、通常は、値のシーケンスを返す For Each ループを使用するか、Count や Max などの標準クエリ演算子を適用することで行われます。このような処理を遅延実行と呼びます。
' Query execution that results in a sequence of values.
For Each number In evensQuery
Console.Write(number & " ")
Next
' Query execution that results in a single value.
Dim evens = evensQuery.Count()
値のシーケンスの場合、取得したデータにアクセスするには、For Each ループの反復変数 (前の例の number) を使用します。クエリ変数 evensQuery にはクエリ結果ではなくクエリ定義が保持されているため、クエリ変数を複数回使用することで、必要に応じて何度でもクエリを実行できます。たとえば、アプリケーションで使用しているデータベースが、別のアプリケーションによって頻繁に更新されるとします。データベースのデータを取得するクエリを作成したら、For Each ループを使用してクエリを繰り返し実行することで、常に最新のデータを取得できます。
次の例は、遅延実行のしくみを示しています。前の例と同様に、evensQuery2 が定義され、For Each ループで実行されますが、その後でデータ ソース numbers の一部の要素が変更されます。次に、2 回目の For Each ループによって evensQuery2 が再実行されます。2 回目は、For Each ループが numbers の新しい値を使用してクエリを再実行するため、結果が異なります。
Dim numberArray() As Integer = {0, 1, 2, 3, 4, 5, 6}
Dim evensQuery2 = From num In numberArray _
Where num Mod 2 = 0 _
Select num
Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8
' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
出力
Evens in original array:
0 2 4 6
Evens in changed array:
0 10 2 22 8
即時実行
クエリの遅延実行では、クエリ定義がクエリ変数に格納され、後で実行されます。即時実行では、クエリは定義時に実行されます。クエリ結果の個々の要素にアクセスする必要のあるメソッドを使用すると、実行が発生します。多くの場合、即時実行は、単一の値を返す標準クエリ演算子の使用によって強制的に行われます。例としては、Count、Max、Average、および First があります。これらの標準クエリ演算子が適用されると、単一の結果を計算して返すために、すぐにクエリが実行されます。単一の値を返す標準クエリ演算子の詳細については、「集計操作」、「要素操作」、および「量指定子操作」を参照してください。
次の例のクエリは、整数の配列に含まれる偶数の数を返します。クエリ定義は保存されず、numEvens は単純な Integer になります。
Dim numEvens = (From num In numbers _
Where num Mod 2 = 0 _
Select num).Count()
Aggregate メソッドを使用しても、同じ結果を得ることができます。
Dim numEvensAgg = Aggregate num In numbers _
Where num Mod 2 = 0 _
Select num _
Into Count()
次のコードに示すように、クエリ (即時実行の場合) またはクエリ変数 (遅延実行の場合) に対して ToList メソッドまたは ToArray メソッドを呼び出すことでも、クエリを強制的に実行できます。
' Immediate execution.
Dim evensList = (From num In numbers _
Where num Mod 2 = 0 _
Select num).ToList()
' Deferred execution.
Dim evensQuery3 = From num In numbers _
Where num Mod 2 = 0 _
Select num
' . . .
Dim evensArray = evensQuery.ToArray()
前の例では、evensQuery3 はクエリ変数ですが、evensList はリストで、evensArray は配列です。
ToList または ToArray を使用して強制的に即時実行する方法は、クエリをすぐに実行し、結果を単一のコレクション オブジェクトにキャッシュする場合に特に便利です。これらのメソッドの詳細については、「データ型の変換」を参照してください。
GetEnumerator メソッド (コレクション オブジェクト) などの IEnumerable メソッドを使用して、クエリを実行することもできます。