LINQ クエリの概要 (C#)

"クエリ" は、データ ソースからデータを取得する式です。 リレーショナル データベース用の SQL や XML 用の XQuery など、データ ソースによってネイティブ クエリ言語は異なります。 開発者は、サポートする必要のあるデータ ソースの種類やデータ形式ごとに、新しいクエリ言語を習得しなければなりません。 LINQ は、さまざまな種類のデータ ソースやデータ形式に対して一貫した C# 言語モデルを提供することにより、この負担を軽減します。 LINQ クエリでは、常に C# オブジェクトを操作することになります。 共通の基本的なコーディング パターンを使用することで、LINQ プロバイダーが利用可能なときに、XML ドキュメント、SQL データベース、.NET コレクション、その他の任意の形式のデータを照会したり変換したりできます。

クエリ操作の 3 つの手順

LINQ クエリ操作はすべて、次の 3 つの手順で構成されます。

  1. データ ソースを取得します。
  2. クエリを作成します。
  3. クエリを実行します。

クエリ操作の 3 つの手順がソース コードでどのように表されるかを次の例に示します。 この例では、わかりやすくするために整数の配列をデータ ソースとして使用していますが、他のデータ ソースを使用する場合にも同じ概念が当てはまります。 この例は、この記事の残りの部分全体を通して参照されます。

// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];

// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

// 3. Query execution.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

次の図は、クエリ操作全体を表しています。 LINQ では、クエリの実行はクエリ自体とは別個のものです。 つまり、クエリ変数を作成しても、データは取得されません。

Diagram of the complete LINQ query operation.

データ ソース

前の例ではデータ ソースが配列で、ジェネリック IEnumerable<T> インターフェイスがサポートされています。 つまり、LINQ でクエリを実行できるということです。 クエリは foreach ステートメントで実行されますが、foreach には IEnumerable または IEnumerable<T> が必要です。 IEnumerable<T> をサポートする型や、ジェネリック IQueryable<T> などの派生インターフェイスは、クエリ可能型と呼ばれます。

クエリ可能型は、変更や特別な処理を行わなくても、LINQ データ ソースとして使用できます。 ソース データがメモリ内にクエリ可能型として存在していない場合、LINQ プロバイダーは、そのような型としてソース データを表す必要があります。 たとえば、LINQ to XML では、クエリ可能な XElement 型に XML ドキュメントが読み込まれます。

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

EntityFramework では、C# クラスとデータベース スキーマの間にオブジェクト リレーショナル マッピングを作成します。 オブジェクトに対するクエリを記述すると、実行時には、EntityFramework によってデータベースとの通信が処理されます。 次の例では、Customers がデータベース内の特定のテーブルを表し、クエリ結果の型 IQueryable<T>IEnumerable<T> から派生しています。

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

それぞれの種類のデータ ソースを作成する方法の詳細については、対応する LINQ プロバイダーのドキュメントを参照してください。 ただし、基本的な規則は単純です。LINQ データ ソースは、ジェネリック IEnumerable<T> インターフェイス、またはこれを継承するインターフェイスをサポートする任意のオブジェクト (通常は IQueryable<T>) です。

Note

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

クエリ

クエリでは、データ ソースからどのような情報を取得するかを指定します。 オプションとして、情報が返される前に、その情報を並べ替え、グループ化し、構造化する方法を指定することもできます。 クエリはクエリ変数に格納され、クエリ式で初期化されます。 C# クエリ構文を使用してクエリを記述します。

前の例のクエリでは、整数の配列からすべての偶数が返されます。 クエリ式には、fromwhere、および select の 3 つの句が含まれています (SQL に詳しい方は、句の順番が SQL での順番とは逆になっていることに気が付いたと思います)。from 句はデータ ソースを指定し、where 句はフィルターを適用し、select 句は返される要素の種類を指定します。 このセクションでは、すべてのクエリ句について詳しく説明しています。 今の段階で重要な点は、LINQ では、クエリ変数自体は何も処理を行わず、データを返さないという点です。 この時点では、後でクエリが実行されるときに結果の生成に必要となる情報が格納されるだけです。 どのようにクエリが構築されるかについては、「標準クエリ演算子の概要 (C#)」をご覧ください。

Note

クエリは、メソッド構文を使用して表すこともできます。 詳細については、「LINQ でのクエリ構文とメソッド構文」を参照してください。

クエリの実行

遅延実行

クエリ変数自体が行うのはクエリ コマンドの格納のみです。 実際のクエリの実行は、foreach ステートメントでクエリ変数が反復処理されるまで延期されます。 この概念を "遅延実行" と呼びます。遅延実行の例を次に示します。

foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

foreach ステートメントでは、クエリ結果の取得も行われます。 たとえば、前のクエリでは、返されるシーケンスの各値が反復変数 num に (一度に 1 つずつ) 格納されます。

クエリ変数自体にはクエリ結果は格納されないので、クエリを繰り返し実行して更新データを取得することができます。 たとえば、別のアプリケーションによって継続的に更新されているデータベースがあるとします。 自分のアプリケーションでは、最新のデータを取得するクエリを 1 つ作成し、それを周期的に実行することで更新結果を取得できます。

即時実行の強制

一連のソース要素に対して集計関数を実行するクエリでは、最初にそれらの要素を反復処理する必要があります。 このようなクエリには、CountMaxAverageFirst などがあります。 これらのメソッドでは、明示的に foreach ステートメントを使用しなくても処理が実行されます。これは、結果を返すためにクエリ自体が foreach を使用する必要があるからです。 これらのクエリでは、IEnumerable コレクションではなく、単一の値が返されます。 次のクエリは、ソース配列に含まれている偶数の数を返します。

var evenNumQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

クエリの即時実行を強制し、その結果をキャッシュするには、ToList メソッドまたは ToArray メソッドを呼び出します。

List<int> numQuery2 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToArray();

クエリ式の直後に foreach ループを配置することでも実行を強制できます。 ただし、ToList または ToArray を呼び出した場合は、単一のコレクション オブジェクトにすべてのデータをキャッシュする処理も行われます。

関連項目