LinqのWhereに対する条件(predicate)を動的に生成するのは、なかなか大変です。
AND検索であればQueryableを少しずつ構築していけばいいのですが、OR検索となるとそうもいきません。
結局のところExpressionTreeを動的に構築していくことになるわけですが、敷居が高く誰でも気軽に採用できる手段はないというのが難点です。

というわけで、これを実現するためのライブラリを作成しました。GitHubにて公開中です。
https://github.com/rizriver/LinqCondition

このライブラリでは、条件をタイプセーフに構築できるという点が特徴です。

概要


LinqConditionは.Net4で作成されたライブラリです。
Linqにおける動的な条件(Predicate)の生成をサポートします。
公開しているソースコードには、このライブラリを使用したサンプルプロジェクトを含んでいます。

解説


ConditionおよびConditionChainクラスで条件を表現します。これらのクラスを用いることで条件を動的に生成することができます。
条件をクラスインスタンスとして構築するため、タイプセーフな実装が行えます。
ただし判定を行う項目名の指定は文字列で行う必要があります。

構築された条件インスタンスは、QueryableおよびEnumerableの拡張されたWhereメソッド等に引き渡すことができます。
  • Condition
  • 一つのインスタンスで一つの条件を表現する。
    例えば「FieldAというフィールドの値が1である」という一つの条件は、次のようになる。
    var condition = Condition.Create("FieldA", 1);

    Createメソッドの第1パラメータはフィールド名、第2パラメータは条件値。
    またメソッドのオーバーロードもいくつか存在し、「Like一致方法(文字列比較の場合に完全一致か、部分一致かなど)」や「比較方法(等値判定か、以上/以下判定かなど)」を指定することも可能。
    // “abc”という文字列での前方一致を条件とする
    Condition.Create("StringField", "abc", LikeTypes.Forward);

  • ConditionChain
  • Condition同士をANDやORで結合することで、複合条件を表現する。
    例えば「FieldAがtrue、もしくはFieldBが100以上」という複合条件の場合は次のように記述。
    var condition1 = Condition.Create("FieldA", true);
    var condition2 = Condition.Create("FieldB", 100, CompareType.GreaterThanOrEqual);
    var condition = ConditionChain.Create(condition1).OrElse(condition2);
条件を生成したら、Whereメソッドへ渡すことで結果が得られる。

指定可能な比較判定方法


値を比較判定する方法として、次のものが利用できます。

比較方法:列挙体 "CompareType" にて提供

  • Equal:等価比較
  • NotEqual:非等価比較
  • LessThan:"小なり" 数値比較
  • LessThanOrEqual:"以下" 数値比較
  • GreaterThan:"大なり" 数値比較
  • GreaterThanOrEqual:"以上" 数値比較

部分一致方法:列挙体 "LikeTypes" にて提供

  • Forward:前方一致
  • Backward:後方一致
  • Part:部分一致

複雑な条件


ConditionとConditionChainを組み合わせによって、図のような複雑な条件連結も可能。

conditionchain

これは例えば次のように記述することができます。
var chain1 = ConditionChain.Create(Condition.Create("A", true)).AndAlso(Condition.Create("B", 1));
var chain2 = ConditionChain.Create(Condition.Create("C", false)).OrElse(Condition.Create("D", 2));
var chain3 = ConditionChain.Create(Condition.Create("E", "foo")).OrElse(Condition.Create("F", 3));

var chain1_2 = chain1.AndAlso(chain2);
var chain1_2_3 = chain1_2.OrElse(chain3);

var result = elements.Where(chain1_2_3);

Empty条件の表現


ConditionChainは「条件がない状態」をEmptyとして表現できるようにしています。
これによりたとえば、「検索系画面において入力があった項目のみを抽出条件にしたい」といったとき次のように記述することができます。
// 条件インスタンスをEmptyで初期化
var conditionBase = ConditionChain.Empty;

// 画面入力されている項目だけを抽出条件とする
if (txtFieldA.Text != "")
{
    conditionBase.AndAlso("FieldA", txtFieldA.Text);
}
if (txtFieldB.Text != "")
{
    conditionBase.AndAlso("FieldB", txtFieldB.Text);
}
:
:
// 抽出実行
var result = elements.Where(conditionBase); // conditionBaseがEmptyだった場合は全件取得される