2009-12-17 5 views
18

여러 필드 (회사 이름, 우편 번호 등)가있는 양식이있어서 사용자가 데이터베이스에서 회사를 검색 할 수 있습니다. 사용자가 둘 이상의 필드에 값을 입력하면 모든 필드를 검색해야합니다. LINQ를 사용하여 데이터베이스를 쿼리하고 있습니다.LINQ 표현식을 하나로 결합하는 방법은 무엇입니까?

지금까지 나는 입력 내용을보고 표현식 목록으로 바꾸는 함수를 작성했습니다. 이제 LINQ 공급자를 통해 실행할 수있는 단일 식으로 해당 목록을 설정하고 싶습니다.

private Expression<Func<Company, bool>> Combine(IList<Expression<Func<Company, bool>>> expressions) 
    { 
     if (expressions.Count == 0) 
     { 
      return null; 
     } 
     if (expressions.Count == 1) 
     { 
      return expressions[0]; 
     } 
     Expression<Func<Company, bool>> combined = expressions[0]; 
     expressions.Skip(1).ToList().ForEach(expr => combined = Expression.And(combined, expr)); 
     return combined; 
    } 

그러나이의 라인을 따라 예외 메시지와 함께 실패 다음과 같이

내 최초의 시도였다 "이진 연산자 그리고 정의되지 ...". 누구든지 이러한 표현을 결합하기 위해 내가해야 할 아이디어가 있습니까?

EDIT : 표현식과 결과를 변수에 함께 할당하는 것을 잊어 버린 선을 수정했습니다. 그 사람들을 가리켜 주셔서 감사합니다.

답변

9

편집 : 제이슨의 대답은 이제 나무보다 표현이 더 풍부 해 졌으므로 삭제했습니다. 그러나 나는 이것을두고 싶었습니다 :

Where 절에 이것을 사용하고 있다고 가정합니다 ... 왜 각 식을 차례로 차례로 호출하지 않을까요?

var query = ...; 
foreach (var condition in conditions) 
{ 
    query = query.Where(condition); 
} 
+1

@ 존 Skeet :'combined'은'Expression'으로 입력됩니다; 당신은'Expression >'으로 반환하기 위해 어떤 일을해야합니다. – jason

+0

첫 번째 코드를 이해하기 쉽다는 점에 동의합니다. 그래서이 코드를 정답으로 만들 것입니다. 그러나 실제로 두 번째 스 니펫을 사용하려고합니다. 정확히 내가 필요로하는 것입니다. 나는 너무 복잡한 작업을하고있었습니다. Jon에게 감사드립니다. – gilles27

+1

아이러니하게도 나는이 두 주석이 모두 쓰여졌을 때 편집을하고 있었지만 사용 된 두 번째 발췌 문장처럼 그대로 남겨 둘 것입니다. –

22

당신은 Expression.AndAlso과 함께 Enumerable.Aggregate를 사용할 수 있습니다 즉, 동일한 효과를 가져야한다. 다음은 일반 버전이있다 :

Expression<Func<T, bool>> AndAll<T>(
    IEnumerable<Expression<Func<T, bool>>> expressions) { 

    if(expressions == null) { 
     throw new ArgumentNullException("expressions"); 
    } 
    if(expressions.Count() == 0) { 
     return t => true; 
    } 
    Type delegateType = typeof(Func<,>) 
          .GetGenericTypeDefinition() 
          .MakeGenericType(new[] { 
           typeof(T), 
           typeof(bool) 
          } 
         ); 
    var combined = expressions 
         .Cast<Expression>() 
         .Aggregate((e1, e2) => Expression.AndAlso(e1, e2)); 
    return (Expression<Func<T,bool>>)Expression.Lambda(delegateType, combined); 
} 

현재 코드는 결코 combined에 할당되지 않은 :

expr => Expression.And(combined, expr); 

는 비트 단위 AND 연산 combinedexpr의 결과 인 새로운 Expression을 반환하지만 combined를 변이하지 않습니다.

+0

+1, 기술적으로 훌륭한 답변입니다. 나는 Jon 's를 받아 들였습니다. 더 단순 해 보이고 실제로 Where의 사용은 실제로 제가해야 할 일입니다. – gilles27

+1

@ gilles27 : 예, 'Where' 절의 술어에만 사용하는 경우 Jon의 대답이 이동 방법입니다. 좀 더 일반적인 버전이 필요하면 내 버전이 도움이 될 것입니다. :-) – jason

0

여기 Linq 표현을 결합하는 일반적인 질문이 있습니다. 나는이 문제에 대한 일반적인 해결책을 가지고있다. 게시 된 특정 문제에 대한 답변을 드릴 것입니다. 그러나 그러한 경우에는 분명히 가지 않을 것입니다. 그러나 귀하의 경우 간단한 해결책이 실패 할 경우이 방법을 사용해보십시오.

먼저 두 가지 간단한 기능으로 구성된 라이브러리가 필요합니다. 그들은 System.Linq.Expressions.ExpressionVisitor을 사용하여 표현식을 동적으로 수정합니다. 핵심 기능은 표현식 내부의 매개 변수를 통합하여 동일한 이름의 두 매개 변수가 동일하게 만들어졌습니다 (UnifyParametersByName). 나머지 부분은 명명 된 매개 변수를 주어진 표현식 (ReplacePar)으로 바꿉니다. 이 라이브러리는 github : LinqExprHelper의 MIT 라이센스와 함께 사용할 수 있지만 직접 작성할 수 있습니다.

라이브러리는 복잡한 표현식을 결합하기위한 아주 간단한 구문을 허용합니다. 읽을 수있는 인라인 람다 식과 동적 표현식 작성 및 합성 기능을 함께 사용할 수 있으며 매우 유용합니다.

private static Expression<Func<Company, bool>> Combine(IList<Expression<Func<Company, bool>>> expressions) 
    { 
     if (expressions.Count == 0) 
     { 
      return null; 
     } 

     // Prepare a master expression, used to combine other 
     // expressions. It needs more input parameters, they will 
     // be reduced later. 
     // There is a small inconvenience here: you have to use 
     // the same name "c" for the parameter in your input 
     // expressions. But it may be all done in a smarter way. 
     Expression <Func<Company, bool, bool, bool>> combiningExpr = 
      (c, expr1, expr2) => expr1 && expr2; 

     LambdaExpression combined = expressions[0]; 
     foreach (var expr in expressions.Skip(1)) 
     { 
      // ReplacePar comes from the library, it's an extension 
      // requiring `using LinqExprHelper`. 
      combined = combiningExpr 
       .ReplacePar("expr1", combined.Body) 
       .ReplacePar("expr2", expr.Body); 
     } 
     return (Expression<Func<Company, bool>>)combined; 
    } 
관련 문제