ASP.NET 3.5コアプログラミング学習ノート(21):LINQクエリ構文

16360 ワード

本質的には、Linq-to-SQLはクラスとメソッドをデータソースオブジェクトにマッピングすることができる.
LINQの概要
ほとんどのアプリケーションは、あるデータ・ウェアハウスを中心にしています.アーキテクチャは、アプリケーションの設計中にオブジェクトを介して問題ドメインをモデリングしてきました.これらのオブジェクトには、データ・アクセス・レイヤに接続するための接続が含まれており、データベースと容易に対話し、オブジェクト・モデルを介してリレーショナル・モードを確立します.しかし、この方法は簡単なアプリケーションには煩雑です.
LINQの登場は、これらの単純なアプリケーションのニーズを解決するために、より高い概念層でデータストレージを操作できる強力で使いやすいツールを提供することです.
  .NET 3.5バージョンのプログラミング言語はローカルタイプ推定が可能であり,varキーワードはそのために導入された.本質的には、実装のタイプを指定せずに変数を宣言できます.コンパイラは、変数に割り当てられた値に基づいて実際のタイプを推定します.タイプ推定は、梱包や解体のような運転時の操作には関与しません.すべての作業はコンパイラによって完全に完了します.つまり、コードが実行される前に完了します.コンパイラは、私たちが使用している実際のタイプを識別し、中間言語コードで特定のタイプの安全な方法で変数を宣言します.ローカルタイプ推定はローカル変数に限られ、メンバー変数タイプをこのように宣言したり、メソッドプロトタイプで使用したりすることはできません.
Linq-to-SQLはO/Rで実現され、通過することができる.NETクラスはSQL Serverデータベースをモデリングします.Linq-to-SQLフレームワークはSQL Serverを対象としており、他のリレーショナル・データベースの処理には使用できません.
共通のクエリー構文
LINQ式は、サポートされているデータソースをクエリーしたり、更新したりすることができます.LINQ式に現れる変数はすべて強いタイプです.
LINQオペレーションを表現するには、2つの方法があります.1つは、from、select、whereなどの新しい言語構造に基づいてクエリーを表現し、2つはLambdaメソッドの呼び出しによって表現します.注:すべてのクエリー式は、コンパイル時にメソッドにマッピングされます.2つの方法は機能が等価で,効率が同じである.次のようになります.

  
    
var data = from c in db.Customers
where c.Country == " Spain "
select c;
//
var data = db.Customers.Where(c => c.Country == " Spain " );

はい.NET 2.0版では、管理言語が強力な構造、すなわち匿名の方法を得た.委任でネーミングメソッドを使用する必要はありません.匿名メソッドを任意の場所で定義でき、匿名メソッドの内部で外部変数にアクセスできます.戻り値のタイプを推定できます.この方式の出現は,クラス内の多くのプライベートメソッドの数を低減した.Lambda式は匿名の方法に対する思想をさらに延長し,より精錬している.
投影演算子
select演算子は、データソースのコンテンツをメモリに投影するために使用され、fromキーとともによく使用されます.
SelectとSelectManyの違いは,前者がオブジェクトの階層型結果,後者がシーケンス型結果を返すことである.たとえば、CustomersとOrdersの2つのテーブル間の関係は、Customersが顧客情報を格納し、Ordersが顧客受注情報を格納することです.次のコードが何を返すか見てみましょう.

  
    
var data = from c in customers
where c.Country == " Spain "
select (c.Orders);

条件を満たす顧客に対して、各オブジェクトがOrderオブジェクトの配列である対応するオブジェクトシーケンスが返されます.次のコードを見てみましょう.

  
    
var data = (from c in customers
where c.Country == " Spain "
select c).SelectMany(c
=> c.Orders);

この場合、1対のマルチリレーションシップを確立する顧客ID情報が重複するシーケンスが得られます.
結合とグループ化
Join演算子は、2つのセットで一致するキーを使用して、両方を結合できます.

  
    
var data = from c in customers
join o
in orders
on c.CustomerID equals o.CustomerID
select
new {c.Name, o.OrderNumber};

SQL言語とは異なり、LINQではjoinのon句が項目の順序を区別し、最初の項目は外層シーケンスから、2番目の項目(equals演算子の右側)は内層シーケンスから、そうでなければコンパイルエラーが発生します.
joinオペレータは、外部レイヤセット(前例ではcustomers)要素を遍歴し、結合されたセット(前例ではorders)の条件が満たされた要素に対して、結果セットに新しい要素を追加します.
この句はouter外結合などの他の結合動作もサポートします.具体的にはouter joinを用いて得られた結果表では,外層要素が内層要素と一致しなくても返される.
サンプルコード:

  
    
var data = from c in customers
join o
in (
from orderInJan97
in orders
where orderInJan97.OrderDate.Value.Year == 1997 &&
orderInJan97.ValueDate.Value.Month
== 1
select orderInJan97
)
on c.CustomerID equals o.CustomerID into groupOrders
select
new { c.CompanyName, OrderTotal = groupOrders.Count() };

Customersコレクションは1997年1月の受注サブセットと結合され、各顧客に基づく受注のセットはgroupOrdersコンテナに一時的に格納されます.一致するキーがグループに見つからない場合は、そのグループは空です.結果テーブルの各レコードは、顧客が所定の期間に発注した合計数を持つ顧客に対応します.
group句は、グループ化されたオブジェクトのシーケンスを返します.グループ化されたオブジェクトは空である可能性がありますが、データ・アイテムが含まれている場合、これらのデータ・アイテムのキー値はグループのキー値と一致します.したがって、パケット操作の出力は、GridViewなどのテーブルフォーマットデータバインドコントロールに直接バインドすることはできません.

  
    
var data = from c in customers
group c.ContactName by
new { City = c.City, Region = c.Region } into g
where g.Count() > 1
select g

この例では、顧客都市と地域をグループ化し、結果を連絡先名セットに追加します.また、各都市/地域に複数の連絡先を持つお客様のみを選択します.複数のプロパティに基づいてグループ化する場合は、匿名タイプ(new演算子)を使用する必要があります.グループが確立されると、T-SQLのHaving句のような他の制限条件をパケット操作に適用するには、by句に対応する条件を表すだけでよい.次の例では、都市名の文字数が5より大きいレコードのみに注目します.

  
    
var data = from c in customers
group c.ContactName by
new { City = c.City, Length = c.City.Length > 5 } into g
where g.Count() > 1
select g;

注意、by句の後のどの文も逐一処理されます.上記のコードは、次のコードとはまったく異なります.

  
    
var data = from c in customers
group c.ContactName by
new { City = c.City.Length > 5 } into g
where g.Count() > 1
select g;

この場合、都市名の文字数は5より大きいが、都市名の一致はチェックされないという条件を満たす必要があります.そのため、Romeの連絡先は無視され、LondonとNew Yorkの連絡先はグループに分類されます.
集約演算
多くのオペレータは、オブジェクトの集約と事前定義の計算を行うことができます.LINQは、Count、Sum、Average、Min、Maxなどの一般的な演算子を提供します.
Sumの例:

  
    
var data = from od in dataContext.Order_Details
where od.OrderID == 10250
select
new {
              od.OrderID,
              OrderAmount
= Sum(od.Quantity * od.UnitPrice)
};

パーティション
Take演算子を使用して、指定したデータの接続要素をシーケンスから取り出すことができます.次のようになります.

  
    
var data = (from c in customers
select c).Take(
10 );

この例では、customersセットの上位10オブジェクトのみを取得します.Take演算子は、クエリーの投影を表すクラスにある組み込み言語構造ではありません.
また、Skip演算子を使用して、シーケンスの先頭に指定されたデータの連続要素をスキップすることもできます.次のようになります.

  
    
var data = (from c in customers
select c).Skip(30).Take(
10 );

この方法を使用して、ページング・クエリーに使用します.
単一要素の決定方法
LINQはFirst演算子とSingle演算子を提供し、条件を満たす各要素と単一要素をそれぞれ返す.
Firstの例:

  
    
var data = (from c in dataContext.Customers
join o
in (from t in dataContext.Orders
where t.OrderDate.Value.Year == 1998 &&
t.OrderDate.Value.Month
== 5
select t)
on c.CustomerID equals o.CustomerID
select
new { Company = c.CompanyName,
Country
= c.Country,
OrderDate
= o.OrderDate }
).First(x
=> x.Country == " USA " );

このクエリは、結果リストを指定した時間内に最初に注文した米国の顧客に制限します.Firstのクエリ結果が空の場合、例外が放出されます.例外の放出を回避するには、FirstOrDefault演算子を使用します.この演算子は、条件を満たすレコードが見つからない場合nullを返します.
1つまたは複数の条件を満たすオブジェクトがある場合でも、First演算子は正常に動作します.一方、Single演算子では、条件を満たすオブジェクトが1つしか存在しない場合、例外が放出されます.SingleOrDefault演算子は、結果が空の場合、例外は放出されませんが、条件を満たす要素が複数ある場合は、無効なアクション例外が放出されます.