前回のエントリは、軽い復習。
ということで本題に入ってみようと思う。
もともと何でExpressionTreeを組み立てたかったのかというと、LINQ to SQLで動的に検索条件を組み立てたい!という願望があったからだと思い出した。
まぁ、要は何か受け取って、TrueかFalseを返すLambdaExpressionが出来ればいいことになる。
とりあえず、1~100の集合から10と20を取り出すものをExpressionTreeにしてみようと思う。
using System;
using System.Linq.Expressions;
using System.Linq;
namespace ExpressionTreeBuildSample
{
class Program
{
static void Main(string[] args)
{
// int型のパラメータi
var param = Expression.Parameter(typeof(int), "i");
// i == 10
var lhs = Expression.Equal(
Expression.Constant(10), param);
// i == 20
var rhs = Expression.Equal(
Expression.Constant(20), param);
// i == 10 || i == 20
var body = Expression.Or(lhs, rhs);
// ラムダ式! i => i == 10 || i == 20
var lambda = Expression.Lambda<Func<int, bool>>(body, param);
// 1~100までの列挙を作って
var nums = Enumerable.Range(1, 100);
// 検索!!
foreach (var i in nums.AsQueryable().Where(lambda))
{
Console.WriteLine(i);
}
}
}
}
AsQueryableを呼んでるのは、IEnumerableでは、WhereにExpressionを渡せないからです。
これを実行すると、10と20が表示される。因みに、普通のラムダ式で書くとこんな感じ。
using System;
using System.Linq.Expressions;
using System.Linq;
namespace ExpressionTreeBuildSample
{
class Program
{
static void Main(string[] args)
{
var nums = Enumerable.Range(1, 100);
foreach (var i in nums.AsQueryable().Where(i => i == 10 || i == 20))
{
Console.WriteLine(i);
}
}
}
}
まぁ固定のクエリなら、ラムダ式使ったほうが楽なのはわかってる!
動的にしたときにどうだ!って話ですよね。
ということで、コンソールから"a"が入力されたら10のみ出力。"b"が入力されたら10と20を出力するという形にしてみた。
using System;
using System.Linq.Expressions;
using System.Linq;
namespace ExpressionTreeBuildSample
{
class Program
{
static void Main(string[] args)
{
// a or b
var input = Console.ReadLine();
if (input != "a" && input != "b")
{
// 入力が正しくない場合は即座に終了
return;
}
// int型のパラメータi
var param = Expression.Parameter(typeof(int), "i");
// i == 10
var body = Expression.Equal(
Expression.Constant(10), param);
if (input == "b")
{
// i == 10 || i == 20
body = Expression.Or(body, Expression.Equal(param, Expression.Constant(20)));
}
// ラムダ式!
var lambda = Expression.Lambda<Func<int, bool>>(body, param);
// 1~100までの列挙を作って
var nums = Enumerable.Range(1, 100);
// 検索!!
foreach (var i in nums.AsQueryable().Where(lambda))
{
Console.WriteLine(i);
}
}
}
}
うん。何か動的に組み立ててる感じが出てきた。これをラムダ式でやろうとすると、2パターン用意しなければならない。
and条件だとWhereに渡すものをわけてやるだけでいいけどorだとそうは行かない。
さて、ここら辺で理想像について語ってみようと思う。理想的には、i == 10やi == 20はラムダ式で書きたい。
だって、簡単だもの。それを動的にandやorで組み立てるイメージ。
// 注)動きません
Expression<Func<int, bool>> lhs = i => i == 10;
Expression<Func<int, bool>> rhs = i => i == 20;
var lambda = Expression.Lambda<Func<int, bool>>(
Expression.Or(lhs, rhs));
もっと言えば…
// 注)全然動きません
Expression<Func<int, bool>> lhs = i => i == 10;
Expression<Func<int, bool>> rhs = i => i == 20;
// lhs || rhsのイメージ
var lambda = lhs.Or(rhs);
くらい書けたほうが直感的だろう。
次は、それを目指してみようと思う。