読者です 読者をやめる 読者になる 読者になる

The LINQ Project (3)

.NET

さてここからはおまちかねの応用編.
通常の Standard Query Operators では末端の IEnumerable の条件を上流の IEnumerable にうまく伝える方法がありません.下の例を見てください.

public static IEnumerable<T> OfType<T>(this IEnumerable source) {
  foreach (object item in source) 
    if (item is T) 
      yield return (T)item;
}

このコードでは,型 T でフィルタリングするという情報が上流の source に伝わらないため,無駄な列挙が行われる可能性があります.これがデータベースに対するクエリであれば,そもそも必要なかった要素まで受け取り,あとでそれを捨てるという羽目になりかねません.
DLinq は Standard Query Operators をオーバーロードすることでこの問題を回避しています*1.DLinq は System.Data.DLinq.QueryExtensions クラスの中に,オーバーロードされた Standard Query Operators を持っています.これらの Standard Query Operators は,Query で表されるクエリオブジェクトと Expression で表されるラムダ式を取り,Query を返します.IEnumerable が Query に,デリゲートがラムダ式にそれぞれ置き換わっていることに注意してください.

Query<T> Where<T>(Query<T> seq, Expression<Func<T, bool>> pred)

そして,データベース上のテーブルを表す System.Data.DLinq.Table クラスは Query から派生しているため,この chain のソースとして使用することができます.
オーバーロードされた Standard Query Operators はクエリビルダーとして働き,受け取ったQuery オブジェクトに自身の条件を付与してゆきます.そして最終的にはデータベースに対して等価な SQL 文が発行されるような Query オブジェクトが生成される,という仕組みです.このときの SQL 文はラムダ式から自動的に合成され,またデータベースへの問い合わせはイテレータの lazy evaluation パターンと同様に必要になるまで遅延されます.こうして,ソースがデータベースであれば,Query expressions は SQL に変換され,データベース上で高速に処理されます.
まったく異なる実装がこういう仕組みで同じ構文に統一されるところがミソなようですね.

*1:とはいっても製品版でどういう実装になるかはまだまだ分かりませんけど