2013-07-02 6 views
6

LINQ에서 개체로 변환 할 때와 마찬가지로 문자열 비교를 대/소문자를 구분하지 않도록 LINQ에서 개체로 바꾸는 ExpressionVisitor를 작성하려고합니다.LINQ 식에서 대/소문자를 구분하지 않는 문자열 비교

편집 중요한 이유 중 하나로 인해 사용자 정의 확장 또는 무언가를 내 표현식에 적용하는 대신 ExpressionVisitor를 사용해야합니다. ExpressionVisitor에 전달되는 표현식은 ASP.Net 웹 API에 의해 생성됩니다 ODATA 레이어, 그래서 그것을 생성하는 방법을 제어 할 필요가 없습니다 (즉,이 ExpressionVisitor 내에서 제외하고 검색 문자열을 소문자 수 없습니다).

LINQ to Entities를 (를) 지원해야합니까? 단지 확장이 아닙니다.

여기까지가 있습니다. 문자열에 대해 "Contains"에 대한 호출을 찾은 다음 해당 표현 내의 구성원 액세스에서 ToLower를 호출합니다.

그러나 작동하지 않습니다. 변경 후 표현을 보면 나에게 맞는 것 같아서 내가 뭘 잘못했는지 모르겠습니다. 나는 VisitMember 방법에서 "표현을 반환"줄에 중단 점을 설정 한 다음 "노드"와 "표현"변수에 "ToString"을 할 경우

public class CaseInsensitiveExpressionVisitor : ExpressionVisitor 
{ 

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (insideContains) 
     { 
      if (node.Type == typeof (String)) 
      { 
       var methodInfo = typeof (String).GetMethod("ToLower", new Type[] {}); 
       var expression = Expression.Call(node, methodInfo); 
       return expression; 
      } 
     } 
     return base.VisitMember(node); 
    } 

    private Boolean insideContains = false; 
    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.Name == "Contains") 
     { 
      if (insideContains) throw new NotSupportedException(); 
      insideContains = true; 
      var result = base.VisitMethodCall(node); 
      insideContains = false; 
      return result; 
     } 
     return base.VisitMethodCall(node); 
    } 

, 브레이크 포인트는 여기에 두 번 공격, 그리고 도착 값의 두 세트는 무엇 :

첫 번째 히트 :

node.ToString() 
"$it.LastName" 
expression.ToString() 
"$it.LastName.ToLower()" 

두 번째 히트 :

node.ToString() 
"value(System.Web.Http.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.String]).TypedProperty" 
expression.ToString() 
"value(System.Web.Http.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.String]).TypedProperty.ToLower()" 

나는이 시점에서 내가 뭘 잘못하고 있는지 알아 내기 위해 표현에 대해 충분히 알지 못한다. 어떤 아이디어?

+1

'string.Equals (string1, string2, StringComparison.InvariantCultureIgnoreCase)'? – Corak

+1

문자열 비교에'ToLower'를 사용하지 마십시오. 오류가 발생하기 쉽습니다 ([Turkey Test] (http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html).)). Corak이 제안한대로 대문자 또는 바람직하게는 String.Equals를 사용하십시오. – keyboardP

+0

내 경우에는 작동하지 않습니다. 첫째, ASP.Net 웹 API에 의해 자동으로 생성되므로 Expression에 대한 제어권이 없습니다. 둘째, 일반적으로 LINQ 문을 래핑하는 데 사용할 수있는 항목이 필요하며 LINQ와 엔터티 및 LINQ- 개체 모두에서 작동합니다. –

답변

2

public class Test 
{ 
    public string Name; 
} 
public class CaseInsensitiveExpressionVisitor : ExpressionVisitor 
{ 

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (insideContains) 
     { 
      if (node.Type == typeof (String)) 
      { 
       var methodInfo = typeof (String).GetMethod("ToLower", new Type[] {}); 
       var expression = Expression.Call(node, methodInfo); 
       return expression; 
      } 
     } 
     return base.VisitMember(node); 
    } 

    private Boolean insideContains = false; 

    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.Name == "Contains") 
     { 
      if (insideContains) throw new NotSupportedException(); 
      insideContains = true; 
      var result = base.VisitMethodCall(node); 
      insideContains = false; 
      return result; 
     } 
     return base.VisitMethodCall(node); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Expression <Func<Test, bool>> expr = (t) => t.Name.Contains("a"); 
     var expr1 = (Expression<Func<Test, bool>>) new CaseInsensitiveExpressionVisitor().Visit(expr); 
     var test = new[] {new Test {Name = "A"}}; 
     var length = test.Where(expr1.Compile()).ToArray().Length; 
     Debug.Assert(length == 1); 
     Debug.Assert(test.Where(expr.Compile()).ToArray().Length == 0); 

    } 
} 
+0

흠 ... .... 내 질문의 끝 부분에 적혀있는 정말 긴 표현식이 포함 된 것으로 전달되는 것과 관련이있을 수 있습니다. "value (System.Web.Http.OData.Query.Expressions.LinqParameterContainer + TypedLinqParameterContainer'1 [System.String]). TypedProperty "를 참조하십시오. 나는 그것이 무엇인지 모르지만, ToLower는 String 타입이기 때문에 여전히 적용됩니다. –

+0

또는 LINQ to Entities가 Expression을 실행한다는 사실과 관련이 있습니까? 표현식을 직접 컴파일/호출하려고하면 "이 메소드는 Entity 인프라에 INQ를 지원하며 코드에서 직접 사용할 수 없습니다."라는 메시지가 나타납니다. –

+0

죄송합니다. OData에 익숙하지 않습니다. – Ben

0

이 같은 extesion 방법을 만들 수 있습니다

public static class Extensions 
{ 
    public static bool InsensitiveEqual(this string val1, string val2) 
    { 
     return val1.Equals(val2, StringComparison.OrdinalIgnoreCase); 
    } 
} 

을 그리고 당신은 다음과 같이 호출 할 수

을 : 나는 당신의 코드에서 샘플 응용 프로그램을 제작하고 작업 보인다
string teste = "teste"; 
string teste2 = "TESTE"; 

bool NOTREAL = teste.Equals(teste2); //FALSE 
bool REAL = teste.InsensitiveEqual(teste2); //true 
+0

감사하지만 작동하지 않습니다. LINQ-to-objects와 LINQ-to-Entities 둘 다에서 사용할 수 있도록 LINQ 문을 래핑 할 수 있도록 ExpressionVisitor를 사용하고 싶습니다. 사용자 지정 확장 InsensitiveEqual()은 LINQ-to-Entities에서 작동하지 않습니다. –

+0

또한 ASP.Net 웹 API Odata 계층에서 표현식을 생성하기 때문에 표현식 생성을 제어 할 수 없습니다. –