2011-03-19 6 views
5

두 개의 LambdaExpressions을 컴파일하지 않고 결합하고 싶습니다. 이는 모습입니다컴파일하지 않고 기존 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)); 
    } 

. 또한 C# 메서드 호출을 지원하지 않는 LINQ to SQL과 같은 쿼리 공급자와 호환되지 않습니다.

가장 좋은 방법은 ExpressionVisitor 클래스를 만드는 것입니다. 그러나 이것은 꽤 일반적인 작업 일 수있는 것 같습니다. 누구든지 이런 종류의 기능을 제공하는 기존의 오픈 소스 코드 기반을 알고 있습니까? 그렇지 않다면 가능한 한 일반적인 것으로 만들기 위해 ExpressionVisitor에 접근하는 가장 좋은 방법은 무엇입니까?

답변

4

것이 가장 좋은 방법인지 모르겠어요,하지만 당신은 그런 일 할 수있는 :

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) 
+0

감사합니다 - 나는 실제로 무엇을거야을 고 말했다. 이것은 좋은 출발점이 될 수도 있습니다. – smartcaveman

+0

@ 토마스, 좋아,이게 내가 생각한거야 : 당신이 제안한 것을해라. 그러나 먼저 LambdaExpression의 ParameterExpressions를 비교하여 중복 된 매개 변수 이름이 없는지 확인한다. 그런 다음'ExpressionVisitor'가'ExpressionType.Invoke'를 방문하여 인수를 선택하고 각 매개 변수 항목을 대체하는 인수 값을 사용하여 새로운'getMemberExpression.Body'를 만듭니다. 그런 다음 LambdaExpression의 본문을 반환합니다. 이게 너 한테 어떻게 들리는거야? – smartcaveman

+0

@smartcaveman, 나는 결국 Invoke를 전혀 필요로하지 않는 ExpressionVisitor 솔루션을 작성했습니다. 내 업데이트 된 답변보기 –

관련 문제