LINQの伝説

 

LINQ

LINQ(Language INtegrated Query)とは、リレーショナルデータベース(RDB)などのデータリソースに対して、オブジェクト指向型言語であるC#から、オブジェクト指向スタイルでのデータ問合せを実現するために、問合せ言語をプログラミング言語に統合したもの、またそのための仕組み。

 

クエリ式:

SQLライクな問い合わせ言語をC#のソースコード中に直接書き込める式。

例)

        class Person

        {

            internal int Age { get; set; }

            internal string Name { get; set; }

            internal string Sex { get; set; }

        }

 

        void Example3()

        {

            Person[] family =

                new Person[] {

                  new Person { Age = 7, Name = "Hinako", Sex = "female" },

                  new Person { Age = 5, Name = "Yuiko", Sex = "female" },

                  new Person { Age = 39, Name = "Kotaro", Sex = "male" },

                  new Person { Age = 46, Name = "Akiko", Sex = "female" },

                };

 

            IEnumerable<Person> girls =

                from p in family

                where p.Sex.Equals("female")

                orderby p.Age

                select p;

 

            foreach (Person g in gs)

                {

                Console.Write("{0}:\t{1} {2}\n", g.Age, g.Name, g.Sex);

            }

        }

 

Personクラスの配列を表すfamily変数を問い合わせ対象とするクエリ式を例に取る。注目はIEnumerable<T>を型に持つクエリ式の部分。

クエリ式は、SQLを知っているものの目からすると、from句ではじまってwhere句が続き、orderby句で整列させ、select句で対象とするフィールドの抽出を実施している、様に見える。

SQLでは通常、SELECT句が先頭に来るが、よくあるデータベースエンジンでは通常FROM句、WHERE句、ORDER BY句の順に処理を実行し、最後に表示項目であるSELECT句を実行するので、LINQは書き方としては理に適っている。

 

クエリ変数:

クエリ式により返される値を格納する変数を「クエリ変数」と呼び、これらはIEnumerable<T>またはその派生型であるIQueryable<T>を持つ。上記例ではgirlsがこれにあたる。

 

標準クエリ演算子:

クエリ式内部で用いられているSQLの予約語に当たる各種キーワード(WhereOrderByなど)は、実のところメソッドの別形式表現で、それらメソッドのことを「標準クエリ演算子」と呼ぶ。

上記の例ではクエリ式はコンパイラにより内部的に以下のメソッド形式に書き換えられている。

            IEnumerable<Person> gs = family.Where(f => f.Sex.Equals("female")).OrderBy(f => f.Age).Select(f => f);

実際、コードの中に上記のメソッド形式を埋め込んでも全く問題なく実行できる。

 

型推論とvar

var型は厳密型を定義しなくとも、右辺値の値からコンパイラが型を推論できる場合に、その型でよいですとプログラマが特に文句を言わず、そのまま使ってしまえる型。

何でも放り込めるVBVariant型やC#の全てのクラスの基本クラスであるobjectとはちがう。右辺値から型を推論するために初期値が必要。また、一度宣言されたvar型の変数の型は厳密に定まり、以後その変数に別の型の値を参照させることは不可。

var i = 1;   //iint

i = 2;

//i = "s";   //stringでないので不可

var s = "a"; //sstring

s = "b";

s = ((char)2).ToString();

 

配列の暗黙的型定義:

配列型を定義するときも右辺値により、型の推論が可能な場合にコンパイラが型を決定してくれる。

int[] ia1 = new int[3];

int[] ia2 = { 1, 2, 3 }; //配列リテラルによる初期化

int[] ia3 = new int[] { 1, 2, 3 };

int[] ia4 = new[] { 1, 2, 3 }; //暗黙的配列

 

匿名クラス:

var型により、匿名のクラスが作成できるようになった。

var anonymous = new { age = 7, name = "Hinako" };

 

匿名クラスはImutable(操作不能)のクラスとなる。

var an1 = new { age = 7, name = "Hinako" };

var an2 = new { age = 5, name = "Yuiko" };

var an3 = new { age = 39, name = "Kotaro", sex = "male" };

an1 = an2; //暗黙定義された型が同じなのでOK

// an1 = an3; //暗黙定義された型が違うのでダメ

 

匿名クラスの制約:

publicのプロパティのみ定義可能

・初期化時に初期値が必要

staticとしては定義不可

・メソッドの定義やオーバーロードは不可

 

型推論を用いたクエリ式:

これまでに述べた型推論、および匿名クラスの利用により、先ほどのクエリ式は、以下の様に書くことも可能。

例)

        void Example1()

        {

            var persons =

                new[] {

                  new { age = 7, name = "Hinako", sex = "female" },

                  new { age = 5, name = "Yuiko", sex = "female" },

                  new { age = 39, name = "Kotaro", sex = "male" },

                  new { age = 46, name = "Akiko", sex = "female" },

                };

 

            var girls =

                from p in persons

                where p.sex == "female"

                orderby p.age

                select new { p.age, p.name, p.sex };

 

            foreach (var g in girls)

            {

                Console.Write("{0}:\t{1} {2}\n", g.age, g.name, g.sex);

            }

        }

 

JOIN

LINQでは複数のリストを結合するJOIN句を表現することも可能。

例)

        class Party

        {

            public string Title { get; set; }

            public string Name { get; set; }

        }

 

        Party[] parties =

            new Party[] {

                new Party { Title = "Mizuho", Name = "Kotaro" },

                new Party { Title = "Jazza", Name = "Akiko" },

                new Party { Title = "Epoch", Name = "Hinako" },

                new Party { Title = "Epoch", Name = "Yuiko" },

                new Party { Title = "Surpass", Name = "Kotaro"},

                new Party { Title = "Surpass", Name = "Akiko"},

                new Party { Title = "Surpass", Name = "Akiko"},

                new Party { Title = "Surpass", Name = "Hinako"},

                new Party { Title = "Surpass", Name = "Yuiko"},

            };

 

        void Example4()

        {

            var statuses =

                from f in persons

                join p in parties on f.Name equals p.Name

                select new { f.Name, p.Title };

 

            foreach (var ss in statuses)

            {

                Console.WriteLine("{0}, {1}", ss.Name, ss.Title);

            }

        }

 

JOIN後の条件追加:

JOINした結果生じた積集合を、一旦別の一時変数に格納し、その一時変数に対してWhereによる述語を適用することも可能。

例)

        void Example5()

        {

            var statuses =

                from f in persons

                join p in parties on f.Name equals p.Name into z

                from a in z

                where a.Name.Equals("Hinako")

                select new { a.Name, a.Title };

 

            foreach (var ss in statuses)

            {

                Console.WriteLine("{0}, {1}", ss.Name, ss.Title);

            }

        }

 

Distinctの適用:

クエリ実施後の結果セットに対してDistinctなどの操作をすることも可能。

例)

        class Belong

        {

            public string Title { get; set; }

            public string Name { get; set; }

            public int Age { get; set; }

 

            public override bool Equals(object obj)

            {

                if (!(obj is Belong)) return false;

                else

                {

                    Belong b = (Belong)obj;

                    return Title.Equals(b.Title) && Name.Equals(b.Name) && Age.Equals(b.Age);

                }

            }

            public override int GetHashCode()

            {

                return Title.GetHashCode() ^ Name.GetHashCode();

            }

 

            public override string ToString()

            {

                return "Title: " + Title + ", Name: " + Name + ", Age: " + Age;

            }

        }

 

        void Example6()

        {

            IEnumerable<Belong> belongs =

                from f in persons

                join p in parties on f.Name equals p.Name

                select new Belong { Title = p.Title, Name = f.Name, Age = f.Age };

 

            IEnumerable<Belong> buniqs = belongs.Distinct();

 

            foreach (Belong bs in buniqs)

            {

                Console.WriteLine("{0}, {1}, {2}", bs.Name, bs.Title, bs.Age);

            }

        }

 

Func型:

C#には標準で一般的によく用いられるメソッド定義のためのデリゲート型としてクラスライブラリに組み込みで定義されているものがあり、そのうちの一つがFunc型である。

     もう一つ代表的なのものに、Action型がある。Func型が戻り値を返すのに対し、Action型は戻り値を返さない。Func型がVBでいうところのFunctionキーワード、Action型がVBでいうところのSubキーワードに類すると考えるとわかりやすいだろうか。

 

delegate TResult Func<in T, out TResult>(T arg)

 Func型はT型を入力引数の型として受け取り、TResult型を戻り値の型として返す一変数関数のためのデリゲート型である。

 注)変数を複数受け取るFunc型の定義も存在する。

 下記の例では、Func<int, int>として引数にintを受け取り、戻り値にintを返すメソッドのデリゲート型変数squareが宣言されており、

 その実体はラムダ式:x => x * x、すなわちint xの二乗を返す式である。

例)

        // ラムダ式:Fx => x * xdFunc<int, int>型の変数に代入

        Func<int, int> square = x => x * x;

 

        // 式形式ではない、ラムダ式もFuncに代入が可能

        Func<int, int> square1 = (x) => { return x * x; };

 

標準クエリ演算子の使用例:

標準クエリ演算子について以下の例を用いながら解説する。

例)

        void Example4()

        {

            List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };

            Func<int, bool> predicate = n => n <= 6;

            Func<int, int> keySelector = n => n % 3; //keySelectorの戻り値型は順序が付けられれば何でもよい

            Func<int, int> keySelector2 = n => 6 - n ;

            Func<int, int> selector = n => n;

            var nums = numbers.Where(predicate).OrderBy(keySelector).ThenBy(keySelector2).Select(selector);

 

            foreach (int i in nums)

            {

                Console.Write(i + " ");

            }

            Console.WriteLine();

        }

 

Where(Func<TSource, Boolean> predicate)

 デリゲート型であるpredicate引数は、TSource型入力値に対してBoolean型の戻り値を返す述語を表す式を要求する。

 上記 Func<int, bool>の実例では、ラムダ式:n => n <= 6が割り当てられている。

 これにより、受け取った入力値の整数n6以下かどうかを判定し、真偽値が返却され、真のもののみが後続の標準クエリ演算子(OrderBy)の新たな入力値IEnumerable<TSource>として渡されてゆく。

 

OrderBy(Func<TSource, TKey> keySelector)

 デリゲート型であるkeySelector引数は、TSource型入力値に対するTKey型キー値を返す式を引数として要求している

 入力値の型はFuncの第一型引数TSourceによって、戻り値であるキー値の型は第二型引数TKeyによって指定されている。

 上記 Func<int, int> keySelectorの実例では、ラムダ式:n => n % 3が割り当てられている。

 これにより、受け取った入力値の整数nに対して、キー値:n % 3、すなわち入力値n3で割った剰余がキー値として返却されることを設定している。

 LINQは戻ったキー値の大小関係により、クエリ結果の並び替えを実施する。

 

ThenBy(Func<TSource, TKey> keySelector)

 上記 Func<int, int> keySelector2では、ラムダ式:n => 6 - n が割り当てられている。

 これにより、受け取った入力値の整数nに対して、キー値:6 - n、すなわち6から入力値n引いた値がキー値として返却されることを設定している。

 

Select(Func<TSource, TResult> selector)

 射影メソッドとも呼ばれるSelectは、selectorデリゲートにより、前出の標準クエリ演算子群から渡されるIEnumerable<TSource>型のデータソースを、TResult型に変換する。

 下記 Func<int, int> selectorでは、ラムダ式:n => n が割り当てられているが、これは受け取った入力値である整数nを、そのまま戻り値としている。

 

LINQによる動的クエリ式の作成:

ここまでは、LINQによるクエリ式の作成および問い合わせを、静的に実施する例を取り上げた。以降、LINQによるクエリ式の動的作成、および問い合わせを実現するためにはどうすればよいか、という問に答えていこうと思う。そのためにまず、ここではやはりLINQと同時にC#に導入された「式ツリー」を取り上げる。式ツリーにより、型安全を保ちつつ動的にラムダ式を生成、組み合わせるための仕組みが提供される。

 

式ツリー:

.NETにおける式ツリーとは、「コードの木構造による表現」であり、木構造におけるそれぞれのノードが「式」を表していて、それら式は「メソッドの呼び出し」であったり「二項演算」であったりする。

例)

-- メソッドA呼び出し

|- x > y

式ツリーにより表現された「コード」は、「コンパイル」や「実行」したりすることができる。これにより、実行コードの動的変換や、様々な種類のデータベースでのLINQクエリの実行、動的クエリの作成などが可能となる。

 

式ツリーによる数式の作成:

 動的ラムダ式の生成の例として、簡単な数式を式ツリーを用いて作成する例を紹介する。

 下記例では、式ツリー型変数Expression<Func<int, int>> squareexprを定義し、初期値としてラムダ式:(x => x * x)を代入している。

一旦定義した式ツリー型変数から、Compileメソッドを用いて動的に式の組み立てを実施して、それをデリゲート型の変数に代入することにより、

 元々ラムダ式で定義されていた式:x => x * xの、式ツリーへの格納を経た後での実行を可能にしている。

例)

delegate int d(int i);

 

        void Example5()

        {

            // Expression<TDelegate>により、ラムダ式を式ツリーに代入することができ、またコンパイルによりデリゲートに戻すことが可能

            Expression<Func<int, int>> squareexpr = x => x * x;

            Func<int, int> f1 = squareexpr.Compile();

            Console.WriteLine(f1(2));

 

            // Expression<TDelegate>TDelegateFuncを用いなくてももちろん可能

            Expression<d> ed = x => x * x;

            d d1 = ed.Compile();

            Console.WriteLine(d1(3));

 

            // コンパイル不可。Expressionには式形式のラムダ式のみ代入可能

            //Expression<Func<int, int>> square2  = (int x) => { return x * x; };

        }

 

ラムダ式のデリゲート、式ツリーへの代入:

Expression<TDelegate>

 Expression型は.NETで式ツリーを表現するための基本型として定義されている。

 一方、Expression<TDelegate>型の変数に代入できるのはラムダ式のみである。

(注意!Expression<TDelegate>型はExpression型の派生クラスでありその目的はラムダ式の格納に特化されている。Expression型が広く一般の式ツリー要素を受け入れるのに対し、Expression<TDelegate>型はラムダ式のみ受け入れる。なぜMSはこの型の名称をLambdaExpression<TDelegate>としなかったのだろうか。。)

 上記例では、Expression<Func<int, int>>型の式ツリー型変数:squareexprが宣言されているが、その割り当て内容として、

 ラムダ式:x => x * xが割り当てられており、このラムダ式は同時にFunc<int, int>型のデリゲート型であることも満たしている。

 

ラムダ式を直接代入せずに、APIを用いて式ツリーを作成

各種Expressionクラスの派生クラスおよびをstaticメソッド用いて、式ツリーを作成することが出来る。

 

クラス例)

ParameterExpression:名前付き変数やパラメタを表すための式

BinaryExpression:二項演算子を持つ式

LambdaExpression:ラムダ式

Expression<Func<int, int>>厳密に型指定されたラムダ式

 

staticメソッド例)

Expression.Parameter(Type, string)

 式ツリー上でパラメタまたは変数を表現するParameterExpression型を作成するstaticメソッド

 引数にパラメタのTypeと、それを表現する文字列を取る(ただし、この文字列表現はデバッグまたはプリントにのみ用いられる)

 下記例では、"x"という名前でint型のTypeを持つParameterExpression型を作成している

 

Expression.Multiply(Expression, Expression)

 乗算を表現するBinaryExpression型を作成するstaticメソッド

 引数に乗算演算子の左項と右項を表現するExpression型の変数を取る

 下記例では、(int x)ParameterExpression型を二乗する二項演算ノードを作成している

 

Expression.Lambda(Expression, ParameterExpression[])

 このメソッドは、System.Func 汎用デリゲートの 1 つから適切なデリゲート型を構築する。

次に、デリゲート型を Lambda ファクトリ メソッドの 1 つに渡して LambdaExpression を作成する。

 作成されたデリゲート型は厳密な型付けが行われていない

 

Expression.Lambda<TDelegate>(Expression, ParameterExpression[])

 このメソッドはまずSystem.Func標準デリゲートの内のどれか適切なデリゲート型を構築する

 それからそのデリゲート型をラムダ式ファクトリメソッドに渡してLambdaExpressionノードを作成する。

 

例)

        void Example6()

        {

            // ラムダ式の作成と実行 (LambdaExpression作成時にデリゲートの厳密型付けを実施しない)

            ParameterExpression param = Expression.Parameter(typeof(int), "x");

            BinaryExpression mult = Expression.Multiply(param, param);

            LambdaExpression lambda = Expression.Lambda(mult, param);

            Func<int, int> square = (Func<int, int>)lambda.Compile();

            Console.WriteLine(square(5));

 

            // ラムダ式の作成と実行 (Expression<TDelegate>作成時にデリゲートの厳密型付け(この場合Func<int, int>)を実施する)

            Expression<Func<int, int>> slambda = Expression.Lambda<Func<int, int>>(mult, param);

            Func<int, int> anothersquare = slambda.Compile();

            Console.WriteLine(anothersquare(6));

 

            // x * yを実現

            ParameterExpression paramx = Expression.Parameter(typeof(int), "x");

            ParameterExpression paramy = Expression.Parameter(typeof(int), "y");

            BinaryExpression multxy = Expression.Multiply(paramx, paramy);

            Expression<Func<int, int, int>> lambdaxy = Expression.Lambda<Func<int, int, int>>(multxy, paramx, paramy);

            Func<int, int, int>  multiply = lambdaxy.Compile();

            Console.WriteLine(multiply(5, 6));

        }

 

式ツリーから別の式ツリーを作成:

(目標)

変形元:x => x * x

変形先:x => x * x + 2

 

LambdaExpression.Body

 ラムダ式の本体(ラムダ式:「x => x * x」の、入力演算子以降の「x * x」の部分)を取得する。

 

Expression.Add(Expression, Expression)

 オーバーフローチェックを行わない算術加算演算を表すBinaryExpressionを作成する。

 

(実行例)

ラムダ式表現(LambdaExpression:x => x * x)から、ラムダ式本体(LambdaExpression.Body:x * x)を抜き取って、

別の式を、Expression.Addメソッドを用いて加算する。

 

        void Example7()

        {

            //ラムダ式を渡す場合、LambdaExpression.Bodyで渡す必要がある

            Expression<Func<int, int>> square = x => x * x;

            Expression body = square.Body;

            BinaryExpression squareplus2 = Expression.Add(body, Expression.Constant(2));

            Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2, square.Parameters);

            Func<int, int> compile = expr.Compile();

            Console.WriteLine(compile(7));

 

            // x => x * x から (x, y) => x * x / yに出来るか?

            ParameterExpression paramy = Expression.Parameter(typeof(int), "y");

            BinaryExpression sqrxdivy = Expression.Divide(body, paramy);

            IEnumerable<ParameterExpression> paramenum = square.Parameters.Concat<ParameterExpression>(new[] { paramy });

            Expression<Func<int, int, int>> expr2 = Expression.Lambda<Func<int, int, int>>(sqrxdivy, paramenum);

            Func<int, int, int> compile2 = expr2.Compile();

            Console.WriteLine(compile2(6, 9));

        }

 

動的LINQの組立:

ここまでで下地が整ったところで、簡単なSQL文をターゲットにしたLINQを動的に組み立てる例を取り上げる。

 

目標:

     from per in Person

     where per.Sex=female, per.Age>=7

の述語にあたる、「Sex=female, Age>=7」の動的組み立て。

 

        void Example7()

        {

            // selector, parameter

            Expression<Func<Person, string>> search = person => person.Name;

            ParameterExpression per = Expression.Parameter(typeof(Person), "per");

 

            // Sex=female

            string sSex = "female";

            MemberExpression mySex = Expression.PropertyOrField(per, "Sex");

            ConstantExpression selSex = Expression.Constant(sSex, typeof(string));

            BinaryExpression condSex = Expression.Equal(mySex, selSex);

 

            // Age>=7

            int ceil = 7;

            MemberExpression upAge = Expression.PropertyOrField(per, "Age");

            ConstantExpression nowAge = Expression.Constant(ceil, typeof(int));

            BinaryExpression condAge = Expression.GreaterThanOrEqual(upAge, nowAge);

 

            // Condition build

            BinaryExpression cond = Expression.AndAlso(condSex, condAge);

            Expression<Func<Person, bool>> pred = Expression.Lambda<Func<Person, bool>>(cond, per);

 

            IQueryable<Person> queryableData = persons.AsQueryable<Person>();

            IQueryable<Person> results = queryableData.Where<Person>(pred);

            IQueryable<string> sresults = results.Select<Person, string>(search);

 

            foreach (string name in sresults)

            {

                Console.WriteLine(name);

            }

        }

 

大まかな手順としては、

1)Expression型により式の右辺と左辺に当たる要素(per.Sex"female"per.Age7)を式ツリー(MemberExpressionConstantExpressionなど)としてそれぞれの型に応じて作成

2)それら要素を式(per.Sex=="female", per.Age>=7)の形にすべく、条件の組立メソッド(Expression.EqualExpression.GreaterThanOrEqualなど)により二項演算式:BinaryExpressionとして組み立てる

3)二項演算式をさらにExpression.AndAlsoにより結合し、全ての個別条件を網羅する全体条件(per.Sex=="female" AndAlso per.Age>=7)を作成

4)全体条件とそれに与えるパラメタを用いて、Expression.Lambdaによりラムダ式(per => ((per.Sex == "female") AndAlso (per.Age >= 7))の式ツリーを作成

5)ラムダ式の式ツリーを述語として、IQueryableに対してWhereメソッドとSelectメソッドを発行するクエリ変数を作成

6)クエリ実施

 

動的LINQJOINを実現:

予めJOIN句を含むIEnumerable<Belong>を作成しておき、Where(pred)にて、述語を動的に付与

また、Containsメソッドの適用によりSQLでのLIKE句を実現

 

目標:

    Title=Surpass, Name LIKE %ko%, Age>=7

 

        void Example8()

        {

            // from, join, select

            IEnumerable<Belong> belBase =

                from f in persons

                join p in parties on f.Name equals p.Name

                select new Belong { Title = p.Title, Name = f.Name, Age = f.Age };

 

            // parameter

            ParameterExpression bel = Expression.Parameter(typeof(Belong), "bel");

 

            // Title=Surpass

            string sTitle = "Surpass";

            MemberExpression myTitle = Expression.PropertyOrField(bel, "Title");

            ConstantExpression selTitle = Expression.Constant(sTitle, typeof(string));

            BinaryExpression condTitle = Expression.Equal(myTitle, selTitle);

 

            // Name LIKE %ko%

            string sName = "ko";

            MemberExpression myName = Expression.PropertyOrField(bel, "Name");

            ConstantExpression selName = Expression.Constant(sName, typeof(string));

            MethodCallExpression condName = Expression.Call(myName, "Contains", null, selName);

 

            // Age>=7

            int ceil = 7;

            MemberExpression upAge = Expression.PropertyOrField(bel, "Age");

            ConstantExpression nowAge = Expression.Constant(ceil, typeof(int));

            BinaryExpression condAge = Expression.GreaterThanOrEqual(upAge, nowAge);

 

            // Condition build

            BinaryExpression cond = Expression.AndAlso(condName, condTitle);

            cond = Expression.AndAlso(cond, condAge);

            Expression<Func<Belong, bool>> pred = Expression.Lambda<Func<Belong, bool>>(cond, bel);

            IQueryable<Belong> belongs = belBase.AsQueryable<Belong>();

            IQueryable<Belong> results = belongs.Where<Belong>(pred);

 

            foreach (Belong b in results)

            {

                Console.WriteLine(b);

            }

        }

 

と、一応出来るには出来るのだが、正直たったこれだけのSQLを書くためにこの量のC#のソースコードが必要となると、生産性には大いに疑問が生じる。

そこで目に留まったのが、下記サイトにて紹介されているPredicateBuilderの存在だ。

 

PredicateBuilder

http://www.albahari.com/nutshell/predicatebuilder.aspx

 

これまでの式ツリーの要素を手作業で一つずつ用意していたのに対し、PredicateBuilderが提供するメソッドではラムダ式自体を結合の単位としている。

また、ラムダ式を用いた式ツリーの作成でラムダ式での結合操作後の本体に適用するパラメタが元のラムダ式のパラメタである必要があるという制約を、Expression.Invokeメソッドを用いることで見事なまでに打開している。これを用いて、前出のクエリを書き換えてみる。

 

        void Example10()

        {

            // from, join, select

            IEnumerable<Belong> belBase =

                from f in persons

                join p in parties on f.Name equals p.Name

                select new Belong { Title = p.Title, Name = f.Name, Age = f.Age };

 

            // ベースとなる述語

            Expression<Func<Belong, bool>> pred = PredicateBuilder.True<Belong>();

 

            // Title=Surpass

            string sTitle = "Surpass";

            pred = pred.And<Belong>(bel => bel.Title == sTitle);

 

            // Name LIKE %ko%

            string sName = "ko";

            pred = pred.And<Belong>(bel => bel.Name.Contains(sName));

 

            // Age>=7

            int ceil = 7;

            pred = pred.And<Belong>(bel => bel.Age >= ceil);

 

            IQueryable<Belong> belongs = belBase.AsQueryable<Belong>();

            IQueryable<Belong> results = belongs.Where<Belong>(pred);

 

            foreach (Belong b in results)

            {

                Console.WriteLine(b);

            }

        }

 

ラムダ式により表現される条件を、PredicateBuilderの拡張メソッドで繋げていくだけの操作で済むので、かなりやっていることが直感的になり、書くべきソースコードの量も圧倒的に少なくて済む。

また、string型のContainsメソッドをbel.Nameに対してラムダ式内で直接適用できることもあり、LIKE句などでさらに記載の簡素化が実現できている。実際の業務ではこちらを使うことになりそうだ。