Как построить LambdaExpression из существующего LambdaExpression без компиляции
Я хочу объединить два лямбда-выражения без их компиляции.
Вот как это выглядит, если я их скомпилирую:
public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
Expression<Func<TContainer,TMember>> getMemberExpression,
Expression<Func<TMember,bool>> memberPredicateExpression)
{
return x => memberPredicateExpression.Compile()(getMemberExpression.Compile()(x));
}
Это, очевидно, не самый быстрый способ получить целевое выражение из предоставленных аргументов. Кроме того, это делает его несовместимым с поставщиками запросов, такими как LINQ to SQL, которые не поддерживают вызовы методов C#.
Из того, что я прочитал, кажется, что лучший подход заключается в создании ExpressionVisitor
учебный класс. Тем не менее, похоже, что это может быть довольно распространенной задачей. Кто-нибудь знает о существующей базе с открытым исходным кодом, которая обеспечивает такую функциональность? Если нет, то как лучше всего подойти к ExpressionVisitor
сделать его как можно более универсальным?
1 ответ
Я не знаю, лучший ли это способ, но вы могли бы сделать что-то подобное:
public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
Expression<Func<TContainer,TMember>> getMemberExpression,
Expression<Func<TMember,bool>> memberPredicateExpression)
{
ParameterExpression x = Expression.Parameter(typeof(TContainer), "x");
return Expression.Lambda<Func<TContainer, bool>>(
Expression.Invoke(
memberPredicateExpression,
Expression.Invoke(
getMemberExpression,
x)),
x);
}
Использование:
var expr = CreatePredicate(
(Foo f) => f.Bar,
bar => bar % 2 == 0);
Результат:
x => Invoke(bar => ((bar % 2) == 0), Invoke(f => f.Bar, x))
Я думаю, что было бы лучше получить что-то вроде x => x.Bar % 2 == 0
, но это, вероятно, будет значительно сложнее...
РЕДАКТИРОВАТЬ: на самом деле это было не так сложно с посетителем выражения:
public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
Expression<Func<TContainer,TMember>> getMemberExpression,
Expression<Func<TMember,bool>> memberPredicateExpression)
{
return CombineExpressionVisitor.Combine(
getMemberExpression,
memberPredicateExpression);
}
class CombineExpressionVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameterToReplace;
private readonly Expression _replacementExpression;
private CombineExpressionVisitor(ParameterExpression parameterToReplace, Expression replacementExpression)
{
_parameterToReplace = parameterToReplace;
_replacementExpression = replacementExpression;
}
public static Expression<Func<TSource, TResult>> Combine<TSource, TMember, TResult>(
Expression<Func<TSource, TMember>> memberSelector,
Expression<Func<TMember, TResult>> resultSelector)
{
var visitor = new CombineExpressionVisitor(
resultSelector.Parameters[0],
memberSelector.Body);
return Expression.Lambda<Func<TSource, TResult>>(
visitor.Visit(resultSelector.Body),
memberSelector.Parameters);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameterToReplace)
return _replacementExpression;
return base.VisitParameter(parameter);
}
}
Это дает следующее выражение:
f => ((f.Bar % 2) == 0)