初めての LINQ クエリの作成 (Visual Basic)

"クエリ" は、データ ソースからデータを取得する式です。 クエリは、専用のクエリ言語で表されます。 これまでに、リレーショナル データベース用の SQL や XML 用の XQuery など、各種データ ソースに合わせてさまざまな言語が開発されてきました。 このため、アプリケーション開発者は、サポートするデータ ソースの種類やデータ形式ごとに、新しいクエリ言語を習得する必要がありました。

統合言語クエリ (LINQ) は、さまざまな種類のデータ ソースやデータ形式のデータを操作するための一貫したモデルを提供することにより、この負担を軽減します。 LINQ クエリでは、操作の対象は常にオブジェクトになります。 共通の基本的なコーディング パターンを使用することで、XML ドキュメント、SQL データベース、ADO.NET データセットおよびエンティティ、.NET Framework のコレクションなど、LINQ プロバイダーを利用できるあらゆるソースまたは形式のデータを照会したり変換したりすることができます。 このドキュメントでは、基本的な LINQ クエリの作成と使用の 3 つのフェーズについて説明しています。

クエリ操作の 3 つのステージ

LINQ クエリ操作はすべて、次の 3 つのアクションで構成されます。

  1. データ ソースまたはソースを取得します。

  2. クエリを作成します。

  3. クエリを実行します。

LINQ では、クエリの実行とクエリの作成が区別されます。 クエリを作成するだけでは、データは取得されません。 この点については、後で詳しく説明します。

以下の例は、クエリ操作を構成する 3 つの要素を示しています。 この例では、デモンストレーション用に便利なデータ ソースとして整数の配列を使用しています。 ただし同じ概念は、他のデータ ソースにも当てはまります。

Note

プロジェクト デザイナー (Visual Basic) の [コンパイル] ページで、 [Option infer][On] に設定されていることを確認します。

' 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

Output:

0 2 4 6

データ ソース

前の例では、データ ソースが配列であるため、暗黙的にジェネリック IEnumerable<T> インターフェイスがサポートされます。 配列を LINQ クエリのデータ ソースとして使用できるのは、そのためです。 IEnumerable<T> をサポートする型や、ジェネリック IQueryable<T> などの派生インターフェイスは、クエリ可能型と呼ばれます。

配列は、暗黙的なクエリ可能型として、変更や特別な処理を行わなくても、LINQ データ ソースとして使用できます。 同じことは、ジェネリックである List<T>Dictionary<TKey,TValue>、その他 .NET Framework クラス ライブラリのクラスなど、IEnumerable<T> をサポートするすべてのコレクション型に言えます。

ソース データにまだ IEnumerable<T> が実装されていない場合、そのデータ ソースに "標準クエリ演算子" の機能を実装するためには、LINQ プロバイダーが必要となります。 たとえば、LINQ to XML は、XML ドキュメントを、クエリ可能な XElement 型に読み込む処理を担います (以下の例を参照)。 標準クエリ演算子について詳しくは、「標準クエリ演算子の概要 (Visual Basic)」を参照してください。

' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")

LINQ to SQL では、まず、デザイン時に手動で、または Visual Studio で Visual Studio の LINQ to SQL ツールを使用して、オブジェクト リレーショナル マッピングを作成します。 オブジェクトに対するクエリを記述すると、実行時に、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 (統合言語クエリ) に関するページを参照してください)。 基本的な規則は単純です。LINQ データ ソースは、ジェネリック IEnumerable<T> インターフェイスか、これを継承するインターフェイスをサポートする任意のオブジェクトです。

Note

非ジェネリック IEnumerable インターフェイスをサポートする ArrayList などの型も、LINQ データ ソースとして使用できます。 ArrayList の使用例については、「方法: LINQ を使用して ArrayList を照会する (Visual Basic)」を参照してください。

クエリ

クエリには、データ ソースまたはソースから取得したい情報を指定します。 また、どのように情報を並べ替え、グループ化、または構造化して返されるようにするかをオプションで指定することもできます。 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

このクエリ式には、FromWhereSelect の 3 つの句が含まれています。 クエリ式の各句の具体的な関数と目的は、「基本的なクエリ操作 (Visual Basic)」で説明しています。 詳細については、「クエリ」を参照してください。 LINQ では多くの場合、クエリの定義はまず変数に格納され、その後実行されます。 前の例の evensQuery のように、クエリ変数はクエリ可能型であることが必要です。 evensQuery の型は IEnumerable(Of Integer) で、コンパイラによってローカル型推論を使用して割り当てられます。

クエリ変数自体は何も処理を行わず、データを返さないという点に注意してください。 あくまでクエリの定義が格納されるだけです。 前の例で、クエリを実行するのは For Each ループです。

クエリの実行

クエリの実行とクエリの作成は分離しています。 クエリを作成することによってクエリは定義されますが、その実行をトリガーするのは別のメカニズムになります。 クエリは、定義後すぐに実行 ("即時実行)" することも、定義を保存しておき、後でクエリを実行 ("遅延実行") することもできます。

遅延実行

一般的な LINQ クエリは、前の例のようなものです。前の例には evensQuery が定義されています。 クエリは作成されても、すぐには実行されません。 その代わりに、クエリ変数 evensQuery にクエリの定義が格納されます。 通常は、一連の値を返す For Each ループを使用するか、標準クエリ演算子 (CountMax など) を適用することによって、クエリを後から実行することになります。 この処理を "遅延実行" と呼びます。

' 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 を再実行します。 For Each ループでクエリを再実行するときは、numbers 内の新しい値が使用されるため、2 回目は異なる結果が得られます。

Dim numberArray() = {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()

Output:

Evens in original array:

0 2 4 6

Evens in changed array:

0 10 2 22 8

即時実行

クエリの遅延実行では、クエリの定義がクエリ変数に格納され、後から実行されます。 即時実行では、クエリがその定義時に実行されます。 クエリ結果の個々の要素にアクセスする必要があるメソッドを適用したときに、実行がトリガーされます。 即時実行は多くの場合、単一の値を返すいずれかの標準クエリ演算子を使用することで強制的に発生します。 たとえば、CountMaxAverageFirst が該当します。 これらの標準クエリ演算子では、シングルトンの結果を算出して返すために、適用された時点ですぐにクエリが実行されます。 単一の値を返す標準クエリ演算子の詳細については、「集計操作」、「要素の操作」、「量指定子操作」を参照してください。

次のクエリからは、整数の配列に格納されている偶数の数が返されます。 クエリの定義は保存されません。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 = evensQuery3.ToArray()

前の例で、evensQuery3 はクエリ変数ですが、evensList はリストで、evensArray は配列です。

ToList または ToArray を使用して即時実行を強制する手法は、クエリを直ちに実行してその結果を単一のコレクション オブジェクトにキャッシュしておくようなシナリオで特に便利です。 これらのメソッドの詳細については、「データ型の変換」を参照してください。

また、IEnumerable メソッド (IEnumerable.GetEnumerator メソッドなど) を使用してクエリの実行を生じさせることもできます。

関連項目