Advent LINQ 2013 : LINQ連載の目次

丁度区切りがついたので、目次を付けておきます


Advent LINQ (1): SplitAsEnumerable
Advent LINQ (2): TextReader.AsEnumerable
Advent LINQ (3): FilesAsEnumerable
Advent LINQ (4): Buffering
Advent LINQ (5): Flatten
Advent LINQ (6): Match
Advent LINQ (7): Count
Advent LINQ (8): ElementAt
Advent LINQ (9): ParallelQuery対応拡張メソッドの実装
Advent LINQ (10): .NET 2.0でLINQを使う
Advent LINQ (11): SQL文を取得する
Advent LINQ (12): リファクタリング可能なメンバ名
Advent LINQ (13): ExpressionとLINQ
Advent LINQ (14): 条件式が実行される場所
Advent LINQ (15): SQL表現可能な式
Advent LINQ (16): 表形式と構造化形式
Advent LINQ (17) : Expressionの評価
Advent LINQ (18) : Expressionの探索
Advent LINQ (19) : Expressionのコンパイルと独立性
Advent LINQ (20) : IEnumerableとIQueryableの相互変換
Advent LINQ (21) : LINQプロバイダー
Advent LINQ (22) : 解釈するクエリの範囲
Advent LINQ (23) : 事前評価可能な式
Advent LINQ (24) : The Provider Core
Advent LINQ (25) : LINQプロバイダーの仕上げ

Advent LINQ 2013 : あとがき

Advent LINQ 2013 : おまけ (Parallel.ForEach)


以前の連載:

LINQは本当に強力だ (1) データ加工の究極の道具
LINQは本当に強力だ (2) クエリの連結と拡張メソッド
LINQは本当に強力だ (3) 拡張メソッドによる拡張
LINQは本当に強力だ (4) アルゴリズムヘルパーメソッド
LINQは本当に強力だ (5) クエリ構文とメソッド構文
LINQは本当に強力だ (6) TextFieldContext
LINQは本当に強力だ (7) 範囲変数と構文の選択
LINQは本当に強力だ (8) 何が並列化されるのか

Advent LINQ 2013 : おまけ (Parallel.ForEach)

Advent LINQ (10): .NET 2.0でLINQを使うの回で、比較的簡単にParallel.ForEachを自前で実装出来ると書いたので、例を見せる。
 
 

namespace System.Threading.Tasks
{
    public static class Parallel
    {
        public static void ForEach<T>(IEnumerable<T> enumerable, Action<T> action)
        {
            Debug.Assert(enumerable != null);
            Debug.Assert(action != null);

            var exceptionList = new List<Exception>();
            using (var wait = new ManualResetEvent(false))
            {
                var count = 1;
                foreach (var item in enumerable)
                {
                    Interlocked.Increment(ref count);

                    ThreadPool.QueueUserWorkItem(new WaitCallback(p =>
                    {
                        try
                        {
                            try
                            {
                                action(item);
                            }
                            catch (Exception ex)
                            {
                                lock (exceptionList)
                                {
                                    exceptionList.Add(ex);
                                }
                            }
                        }
                        finally
                        {
                            if (Interlocked.Decrement(ref count) == 0)
                            {
                                wait.Set();
                            }
                        }
                    }));
                }

                if (Interlocked.Decrement(ref count) == 0)
                {
                    wait.Set();
                }

                wait.WaitOne();
            }

            if (exceptionList.Count >= 1)
            {
                throw new AggregateException(exceptionList);
            }
        }

        public static void Invoke(params Action[] actions)
        {
            ForEach(actions, action => action());
        }
    }
}

コンパイルを通すには、他にもActionクラスが必要だ。このサンプルはVSCoverageToEmmaで実装したもので、以下の場所に完全なコードがある。

VSCoverageToEmma/VSCoverageToEmma.Core/Tasks/Parallel.cs

Advent LINQ 2013 : あとがき

むちゃくちゃ大変でした。

思い立ったのは11月の下旬、何だか周りでAdvent Calendarのひそひそ声が聞こえ始めた頃です。Advent Calendarをやった事が無かったので、「ふむ、25日まで1日ずつネタを書けばいいのか、簡単じゃないか。やってみるか、一人で」などと考えたのが悲劇の始まりだった。

まず、記事のさじ加減が分からない。「まぁ、LINQなら何でもネタがあるよね。そうだ、縛りを入れるか。一つ一つの記事がある程度独立していて、かつ、短く、読者が応用を想像できるような内容にするってのはどうだろうか?」

LINQは現場で、「当たり前に使っている」か、「全く使っていないか」の両極端に感じる。twitterで流れてくるものの中には、「LINQ禁止」とか、あり得ない現場もあるようだ。
そんなわけで、あんまり専門寄りに寄ってしまってもあれだから、少しでも底辺を持ち上げて、「LINQって何?」な人口を減らす事が出来たら良いなと思った。
(まあ、これは前の連載でも意識していたけど)

で、書き始めたのだが… すぐに壁にぶち当たった。

既に自分の中ではLINQが「空気」状態だった。「空気について説明する」が如く、フォーカスする対象が無いのだ。例えば、きわめて具体的なシチュエーションであれば、いくらでもLINQによる解決策を提示できる。しかし、具体例を示し過ぎると、読者が応用を想像しにくくなってしまう。あくまで、ネタの背景が分かるようにし、かつサジ加減を調整してあまり具体的過ぎる例にしないように(時にはわざとアホな方に振ったり)する必要があった。そして、それに耐えうるネタでなければならない…

10回目ぐらいまではパパッと思いついたネタも、その後が続かない。初めはばりばり書いて書き溜めておいたので良かったのだが、本業が忙しいこともあり、段々ストックが底をついてくる。金を貰っているわけでもないのにこのプレッシャー!!

そして、ふらふらと、やる気の無かったIQueryableネタをやり始めた。もうヤバい。何がって、この後は、アレだ。禁断のネタ「LINQプロバイダー」しかないじゃないか。

LINQプロバイダーなら、このネタだけで最後まで続けられるという点では、ネタ詰まりは回避できる。ただ、LINQプロバイダーは複雑すぎて、色々説明した後でないと、本題に入れない。これでは、1話毎に完結させるという縛りを満たすことがとても難しい。おまけに複雑さを低減するために、どうしても図を書かなければならない。

この迷い、15回ぐらいまで続く。少し読み返してみると、苦悩ぶりが伺えるw

さらに、NGK2013BでLT登壇したあおりを受け、あろうことか、JenkinsのAdvent Calendarにポチっとしてしまった… この状態で気がふれたとしか思えない。何故書く気になったのか、未だに良く分からない…

そして、ネタ考案の時間切れと半分ヤケとで、LINQプロバイダーをやらざるを得なくなり、腹を括って突入。書くからには、LINQ to SQLやLINQ to Entitiesがブラックボックス過ぎて理解不能と思っている方に向けて、少しでも「仕掛け」が見えるようにする必要があるだろうと。

AdventCalendarそんなわけで、後半は特に検証とかやったつもりですが、間違いがあったらゴメンナサイ。頑張って、強く縛りを意識して書いてみました。

右側のカレンダーを見ると、すげーなー、我ながら良く書いたよなぁ、と思います。
本日は23日の夜中、とりあえず、風呂入って寝ますw

Happy Merry Christmas!!

Advent LINQ (25) : LINQプロバイダーの仕上げ

前回のLocationFinderクラスによって、検索条件となる文字列の一覧が得られた。この検索条件をTerraServer WCFインターフェイスを使用して、サービスに送信する。すると、結果レコードがPlaceクラスに入れられて返される。結果が複数のレコードとなる可能性があり、WCFメソッドの戻り値はPlaceの配列で定義されている。
ExpressionTreeModifierPath
とうとう結果が得られたのだ。これをそのまま返せばよい。が、しかし、今まで扱ってきたのは、あくまでIQueryableによるLINQクエリだ。IEnumerableならそのまま返せばよい(配列はIEnumerableを実装しているので)が、少なくともIQueryableでなければならない。
であれば話は簡単だ。配列をAsQueryableすれば良い。

// Get the place name(s) to query the Web service with.
LocationFinder lf = new LocationFinder(lambdaExpression.Body);
List<string> locations = lf.Locations;
if (locations.Count == 0)
    throw new InvalidQueryException("You must specify at least one place name in your query.");
// Call the Web service and get the results.
Place[] places = WebServiceHelper.GetPlacesFromTerraServer(locations);
// Copy the IEnumerable places to an IQueryable.
IQueryable<Place> queryablePlaces = places.AsQueryable<Place>();

結果のIQueryableは、もはやTerraServer LINQプロバイダーの手を離れ、LINQ to ObjectsのLINQプロバイダーが処理を行うようになる。


まだ終わりではない

最初にInnerMostWhereFinderクラスで、最も内側のWhereを探索した事を覚えているだろうか? いま、一息ついたところだが、ここまでの処理で実現したのは、その「最も内側のWhere条件」だ。その外側のLINQクエリは放置されている。これを処理しなければならない。
しかし、前述のようにもうTerraServerは関係ないため、配列をAsQueryableした結果を使って続きのクエリを実行させる必要がある。これを行わせるのが、「ExpressionTreeModifier」クラスだ。このクラスもExpressionVisitorを継承している。やっていることは単純だ。TerraServerのコンテキストクラスのインスタンスを、上記配列をAsQueryableしたものに挿げ替える。
生成されたExpressionは、もはやTerraServerコンテキストを含まない。かつ、サービスから得られた結果を配列で含んでいるため、Expressionツリー全体がLINQ to Objectsで評価可能な状態にある。CreateQueryを呼び出してIQueryableを生成すれば、そのインスタンスはLINQ to ObjectsのLINQプロバイダーが管理する。
こうして、LINQ to TerraServerが完成した。