したがって、これがIQueryable
ではなく のIEnumerable
場合、関数ではなく式を操作する必要があります。まず、各演算子の文字列バージョンを含む辞書を定義し、それをその操作を表す式にマップします。
var mappings = new Dictionary<string, Expression<Func<int, int, bool>>>()
{
{">", ( Expression<Func<int, int, bool>>)((a,b)=> a > b)},
{"<", ( Expression<Func<int, int, bool>>)((a,b)=> a < b)},
{"==", ( Expression<Func<int, int, bool>>)((a,b)=> a == b)},
};
他の操作を追加できます。
ある式全体で、ある式のすべてのインスタンスを別のインスタンスに置き換えるために使用するヘルパー メソッドもあります。
このビジターを使用します。
public class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
そして、それをよりクリーンな構文でラップするだけです:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
そこから問題の核心に移り、特定の演算子を使用して 2 つの特定のオペランドが評価される特定のクエリをフィルター処理する操作を作成します。フィルタリングするクエリ、各オペランドのセレクター、そして操作が必要です。次に、操作のパラメーターのすべてのインスタンスをオペランド セレクターに置き換え、右のセレクターのパラメーターを左のセレクターのパラメーターに置き換えます。これにより、最終製品に 1 つの実際のパラメーターが存在し、ラムダにラップされて渡されます。にWhere
:
public static IQueryable<TIn> WhereOperator<TIn, TLeft, TRight>(
this IQueryable<TIn> query,
Expression<Func<TIn, TLeft>> leftSelector,
Expression<Func<TIn, TRight>> rightSelector,
Expression<Func<TLeft, TRight, bool>> operation)
{
var newRightBody = rightSelector.Body.Replace(rightSelector.Parameters[0],
leftSelector.Parameters[0]);
var newOperator = operation.Body.Replace(operation.Parameters[0], leftSelector.Body)
.Replace(operation.Parameters[1], newRightBody);
var lambda = Expression.Lambda<Func<TIn, bool>>(newOperator,
leftSelector.Parameters[0]);
return query.Where(lambda);
}
使用例:
IQueryable<Tuple<int, int>> query = new[] { Tuple.Create(1, 2) }
.AsQueryable();
var query2 = query.WhereOperator(pair => pair.Item1, pair => pair.Item2
, mappings[">"]);
この場合、メモリ内クエリ可能オブジェクトを使用していますが、後で説明query2.Expression
するように、これは実際には以下を呼び出した場合と同じように見えます。
var query3 = query.Where(pair => pair.Item1 > pair.Item2);