LINQ to Entities でのクエリ
クエリは、データ ソースからデータを取得する式です。一般に、クエリは専用のクエリ言語で表現されます。たとえば、リレーショナル データベースであれば SQL、XML であれば XQuery が使用されます。そのため、開発者はクエリの対象となるデータ ソースやデータ形式ごとに新しいクエリ言語を習得する必要があります。統合言語クエリ (LINQ) は、データ ソースや形式の違いを意識することなくデータを扱うことのできる、より簡素化された一貫したモデルを提供します。LINQ クエリでは、常にプログラミング オブジェクトを操作することになります。
LINQ のクエリ操作は、データ ソースを取得し、クエリを作成して、クエリを実行するという 3 つのアクションから成ります。
LINQ を介したクエリは、IEnumerable ジェネリック インターフェイスまたは IQueryable ジェネリック インターフェイスを実装するデータ ソースに対して行うことができます。ジェネリック IQueryable インターフェイスを実装するジェネリック ObjectQuery クラスのインスタンスは、LINQ to Entities クエリのデータ ソースとして動作します。ObjectQuery ジェネリック クラスは、型指定されたエンティティのインスタンスまたはコレクションを返すクエリを表します。開発者は、CLR オブジェクトとして エンティティ データ モデル (EDM) と対話するための主要なクラスである ObjectContext からインスタンスを作成します。
クエリでは、データ ソースから取得する情報を正確に指定できます。また、並べ替え、グループ化、整形方法を指定して情報を取得することもできます。LINQ では、クエリが変数に格納されます。クエリが一連の値を返す場合、クエリ変数そのものがクエリ可能な型であることが必要です。このクエリ変数は、クエリの情報を保存するだけで、なんらかのアクションを実行したり、データを返したりすることはありません。クエリを作成した後、データを取得するには、そのクエリを実行する必要があります。
一連の値を返すクエリでは、クエリ変数そのものはクエリ結果を保持しません。クエリ変数には、クエリのコマンドが格納されるだけです。クエリ変数が foreach ループまたは For Each ループで反復処理されるまで、クエリは実行されません。これは遅延実行として知られています。つまり、作成されたクエリはすぐには実行されません。これは、任意のタイミングでクエリを実行できるということを意味します。これは、たとえば他のアプリケーションによって更新されるデータベースがある場合に便利です。アプリケーションで、最新情報を取得するクエリを作成し、それを繰り返し実行することにより、更新のたびに最新の情報を取得できます。
遅延実行によって一連の値を返すクエリとは対照的に、シングルトン値を返すクエリは直ちに実行されます。シングルトン クエリの例としては、Count、Max、Average、First があります。結果を計算するためにはクエリ結果が必要であるため、これらのシングルトン クエリは直ちに実行されます。シングルトン値を生成しないクエリでも、ToList メソッドまたは ToArray メソッドを使用することによって、即時実行を強制できます。即時実行を強制するこの手法は、クエリの結果をキャッシュする場合などに使用すると効果的です。クエリの遅延実行と即時実行の詳細については、「LINQ の概要」を参照してください。
クエリ
LINQ to Entities クエリは、クエリ式の構文とメソッド ベースのクエリ構文という 2 とおりの構文を使って作成できます。クエリ式の構文は、C# 3.0 および Visual Basic 9.0 で新たに導入され、Transact-SQL や XQuery などと同様な宣言型の構文で記述された一連の句で構成されます。ただし、.NET Framework の共通言語ランタイム (CLR) は、クエリ式の構文そのものを理解することはできません。そのため、クエリ式はコンパイル時に、CLR が理解できる形式 (メソッド呼び出し) へと変換されます。これらのメソッドは標準クエリ演算子として知られています。開発者は、クエリ構文を使う代わりに、メソッド構文を使ってそれらを直接呼び出すこともできます。詳細については、「クエリ構文とメソッド構文 (LINQ)」を参照してください。標準クエリ演算子の使用方法の詳細については、「LINQ の一般的なプログラミング ガイド」を参照してください。
クエリ式の構文
クエリ式は宣言型のクエリ構文です。開発者は、Transact-SQL に似た形式の高級言語でクエリを記述できます。クエリ式の構文を使用することにより、フィルタ、並べ替え、グループ化など、データ ソースに対するきわめて複雑な処理を最小限のコードで実行できます。詳細については、「LINQ クエリ式 (C# プログラミング ガイド)」および「基本的なクエリ操作 (Visual Basic)」を参照してください。
次の例では、Select を使用して Product テーブルからすべての行を取得し、製品名を表示しています。
Using AWEntities As New AdventureWorksEntities
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productNames = _
From p In products _
Select p.Name
Console.WriteLine("Product Names:")
For Each productName In productNames
Console.WriteLine(productName)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<string> productNames =
from p in products
select p.Name;
Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}
メソッド ベースのクエリ構文
LINQ to Entities クエリを作成するもう 1 つの方法として、メソッド ベースのクエリがあります。メソッド ベースのクエリ構文は、LINQ の演算子メソッドを、ラムダ式をパラメータとして直接順次呼び出すものです。詳細については、「ラムダ式 (C# プログラミング ガイド)」を参照してください。
次の例では、Select を使用して Product テーブルからすべての行を取得し、製品名を表示しています。
Using AWEntities As New AdventureWorksEntities
Dim productNames = AWEntities.Product.Select(Function(p) p.Name)
Console.WriteLine("Product Names:")
For Each productName In productNames
Console.WriteLine(productName)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<string> productNames = products.Select(p => p.Name);
Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}
クエリの作成
前述したように、一連の値を返すように設計されたクエリでは、クエリ変数自体にはクエリ コマンドのみが格納されます。クエリに即時実行を促すメソッドが含まれていなければ、foreach ループまたは For Each ループでクエリ変数を反復処理するまで、実際にはクエリは実行されません。遅延実行により、複数のクエリを組み合わせたり、クエリを拡張したりすることが可能となります。クエリを拡張して新しい操作を追加すると、その変更が最終的な実行時に反映されます。次の例の最初のクエリでは、すべての製品が返されます。2 つ目のクエリでは、サイズが "L" のすべての製品を返すように、Where を使って 1 つ目のクエリを拡張しています。
Using AWEntities As New AdventureWorksEntities()
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productsQuery = _
From p In products _
Select p
Dim largeProducts = _
productsQuery.Where(Function(p) p.Size = "L")
Console.WriteLine("Products of size 'L':")
For Each product In largeProducts
Console.WriteLine(product.Name)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productsQuery =
from p in products
select p;
IQueryable<Product> largeProducts = productsQuery.Where(p => p.Size == "L");
Console.WriteLine("Products of size 'L':");
foreach (var product in largeProducts)
{
Console.WriteLine(product.Name);
}
}
クエリが実行された後、後続のすべてのクエリでメモリ内の LINQ 演算子が使用されます。foreach ステートメントや For Each ステートメントを使用することによって、または LINQ 変換演算子の 1 つを呼び出すことによって、クエリ変数を反復処理すると、即時実行が発生します。これらの変換演算子には、ToList、ToArray、ToLookup、および ToDictionary などがあります。
次の例の最初のクエリでは、すべての製品が返されます。2 番目のクエリは、実行されて赤色の製品を返した後、最初のクエリを拡張します。
Using AWEntities As New AdventureWorksEntities()
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productsQuery = _
From p In products _
Select p
Console.WriteLine("The list of products:")
For Each Product As Product In productsQuery
Console.WriteLine(Product.Name)
Next
Dim redProducts = productsQuery _
.Where(Function(p) p.Color = "Red") _
.Select(Function(p) p)
Console.WriteLine("")
Console.WriteLine("The list of red products:")
For Each redProduct As Product In redProducts
Console.WriteLine(redProduct.Name)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productsQuery =
from p in products
select p;
Console.WriteLine("The list of products:");
foreach (Product product in productsQuery)
{
Console.WriteLine(product.Name);
}
IQueryable<Product> redProducts = productsQuery
.Where(p => p.Color == "Red")
.Select(p => p);
Console.WriteLine("");
Console.WriteLine("The list of red products:");
foreach (Product redProduct in redProducts)
{
Console.WriteLine(redProduct.Name);
}
}