2014-02-05 2 views
0

우선 내가이 문제를 만들기 전에 다른 질문과 답변을 검색했음을 알리고 싶지만 내가 직면하고있는이 특정 문제에서 나를 도울 수있는 어떤 것도 찾지 못합니다.두 표현식 트리에서 일반 람다를 만드는 방법은 무엇입니까?

클래스의 두 속성을 기반으로 레지스터를 필터링해야하는데, 그 중 하나는 검색에 해당하는 필드이고 다른 하나는 데이터베이스에서 레지스터를 참조해야하는 다른 엔터티의 숫자 코드입니다.

내 검색 기능은 다음과 같은 서명이있다 :

public List<TView> SearchByField(int parentCode, string fieldName, string filter); 

나는이 사용하는 표현의 나무를 구현하기 위해 노력하고, 두 식을 얻기 위해 그것을 가지고,하지만 지금은에 이러한 식을 결합하지 않았다했습니다 완성품에 전달하기 위해 하나 만들어라. 단 하나의 표현을 너무 섞을 것이다. 내가 지금까지 코드가 (긴 조각 미안하지만 난이 쉽게 질문을 이해할 수 있도록하는 것이 필요하다고 생각) 아래되었다있어 무엇

: 어떤 제안에 대한

public List<TView> SearchPerField(int parentCode, string fieldName, string filter) 
    { 
     var lambdaExpression = GetLambdaExpressionForSearchByField(fieldName, filter, parentCode); 

     return new PersistenciaImpl<TView>().Where(lambdaExpression).ToList(); 
    } 

    private Expression<Func<TView, bool>> GetLambdaExpressionForSearchByField(string fieldName, string filter, int parentCode) 
    { 
     Expression<Func<TView, bool>> textFilterExpression = GetTextFilterExpression(fieldName, filter); 

     Expression<Func<TView, bool>> parentCodeFilterExpression = GetParentCodeFilterExpression(parentCode); 

     Expression.Lambda<Func<TView, bool>>(textFilterExpression, parentCodeFilterExpression); 

     // THIS IS THE POINT. HOW TO MAKE THIS WORK? 
     Expression.AndAlso(parentCodeFilterExpression, textFilterExpression); 

     return textFilterExpression; 
    } 

    private Expression<Func<TView, bool>> GetParentCodeFilterExpression(int parentCode) 
    { 
     ParameterExpression parameter = Expression.Parameter(typeof(TView), "x"); 

     Expression parent = Expression.Property(parameter, "Parent"); 

     Expression parentCodeExpression = Expression.Property(parent, "Code"); 

     Expression target = Expression.Constant(parentCode); 

     Expression containsMethod = Expression.Call(parentCodeExpression, "Equals", null, target); 

     Expression<Func<TView, bool>> textFilterExpression = 
      Expression.Lambda<Func<TView, bool>>(containsMethod, parameter); 

     return textFilterExpression; 
    } 

    private Expression<Func<TView, bool>> GetTextFilterExpression(string fieldName, string filter) 
    { 
     ParameterExpression parameter = Expression.Parameter(typeof(TView), "x"); 

     Expression property = Expression.Property(parameter, fieldName); 

     Expression target = Expression.Constant(filter.ToUpper()); 

     Expression containsMethod = Expression.Call(property, "Contains", null, target); 

     Expression<Func<TView, bool>> textFilterExpression = 
      Expression.Lambda<Func<TView, bool>>(containsMethod, parameter); 

     return textFilterExpression; 
    } 

감사합니다.

답변

0

나는이 사용하는 표현의 나무를 구현하기 위해 노력하고, 두 식을 얻기 위해 그것을 가지고 있지만, 지금은 최종

에 전달할 하나를 구축하는 이러한 식을 결합하지 않았다했습니다 첫째, 최종 (외부) 람다에 대한 매개 변수를 선언해야합니다. 그런 다음 각각에 동일한 인수를 전달 독립적으로 두 개의 필터 (내부) 람다를 호출 할 필요가 : 당신이 엔티티 프레임 워크 같은 쿼리 제공자와 호환 될 수 있도록 이러한 식을해야하는 경우

// using E = System.Linq.Expressions.Expression; 

var item = E.Parameter(typeof(TView)); 

var combined = E.Lambda<Func<TView, bool>>(
    E.AndAlso(
     E.Invoke(parentCodeFilterExpression, item), 
     E.Invoke(textFilterExpression, item)), 
    item); 

는 상황이 조금 지저분를 얻을 수 Invoke 표현식이 지원되지 않기 때문입니다.당신이에서 내부 표현을 구성하는 경우,

// using E = System.Linq.Expressions.Expression; 

sealed class ParameterReplacementVisitor : ExpressionVisitor 
{ 
    private readonly IDictionary<E, E> _replacements; 

    public ParameterReplacementVisitor(IDictionary<E, E> replacements) 
    { 
     _replacements = replacements; 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     E replacement; 

     if (_replacements.TryGetValue(node, out replacement)) 
      return this.Visit(replacement); 

     return base.VisitParameter(node); 
    } 
} 

// ... 

var item = E.Parameter(typeof(TView)); 

var visitor = new ParameterReplacementVisitor(
    new Dictionary<E, E> { 
     { parentCodeFilterExpression.Parameters[0], item }, 
     { textFilterExpression.Parameters[0], item } 
    } 
); 

var combined = E.Lambda<Func<TView, bool>>(
    E.AndAlso(
     visitor.Visit(parentCodeFilterExpression.Body), 
     visitor.Visit(textFilterExpression.Body)), 
    item); 

다른 방법 : 수동으로 각 필터의 ​​몸을 걷고 외부 람다의 매개 변수에 대한 참조로 내부 매개 변수 참조를 교체 할 필요 두 개의 필터 람다를 인라인 할 것이다 닫힌 환경에서 귀하의 게시물을 제안, 당신은 단순히 내부 표현식을 구성하는 메서드에 인수로 바깥 쪽 람다 매개 변수를 전달하고, 몸체를 반환 할 수 있습니다 (lambdas에서 내부 필터를 래핑하지 않아도 됨).

+0

사람들이 말하는 것처럼 "매력처럼 작동합니다". 당신이 첫 번째로 잘린 것은 트릭을했습니다. 감사합니다! 그냥 궁금해 : 매개 변수는 무엇에 사용됩니까? 나는 디버거를 살펴 봤는데 표현식의 일부를 "Param_0"과 같이 표현했지만, 전체 프로세스에서 어디에 사용 되었는가, 심지어 그것이 의미하는 바에 대한 단서도 없다. 하지만 어쨌든 코드가 작동하고 문제가 해결되었습니다. 다시 한 번 감사드립니다. 단지 내가 어떤 코드가 무엇을하는지 이해할 수 있도록 커버 아래에서 무슨 일이 일어나고 있는지 알고 싶습니다. –

+0

'Func <,>'은 하나의 매개 변수를 취하므로'Func <,>'을 나타내는 람다 식을 만들 때 전달 된 값을 나타 내기 위해 매개 변수를 선언해야합니다. –

0

당신은 식에 Compile method을 사용하여 TDelegate로의 Expression<TDelegate>를 컴파일 할 수

Expression<Func<TView, bool>> lambdaExpression = 
    GetLambdaExpressionForSearchByField(fieldName, filter, parentCode); 
Func<TView, bool> func = lambdaExpression.Compile(); 

을 당신이 Where 함수의 매개 변수로 사용할 수 있습니다되면. 나는 당신이이 같은 필요가 있다고 생각

return new PersistenciaImpl<TView>().Where(func).ToList(); 
+0

그러나 두 표현식을 어떻게 하나만 결합 할 수 있습니까? 질문에 언급하지 않았지만 Expression.AndAlso (parentCodeFilterExpression, textFilterExpression); 컴파일되지 않습니다. –

+0

코드에서 그 시점에 단 하나의 표현 만 있습니다. 'lambdaExpression'은 내부적으로'GetParentCodeFilterExpression'에 의해 반환 된 표현식을 사용합니다. 그러나 그것은 중요하지 않습니다. – Dirk

+0

네, 맞습니다. 이 시점에 도달하면 코드가 솔루션이되지만 문제는 GetLambdaExpressionForSearchByField() 메서드가 아직 작동하지 않도록하는 것입니다. 제 질문은별로 좋지 않았습니다. 미안합니다. 나는 그것을 더 분명하게해야했다. 질문을 편집 했으므로 혼동을 덜 일으킬 수 있습니다. 그러나 귀하의 답변과 의견에 감사드립니다. –

0

를 사용하면 위의 코드와


:

MethodCallExpression where = Expression.Call((
      typeof(Queryable), 
      "Where", 
      new Type[] { TView }, 
      lambdaExpression); 

나는이 솔루션 고려하지 않음을 유의하시기 바랍니다을; 그것은 단지 아이디어 또는 예제 일뿐입니다. 어쩌면 this link 당신을 도울 것입니다.