2013-07-02 35 views
6

Tôi đang cố gắng viết ExpressionVisitor để bao quanh các biểu thức LINQ-to-object để tự động so sánh chuỗi trường hợp không nhạy cảm, giống như chúng nằm trong LINQ-to-entity.Chuỗi phân biệt chữ hoa chữ thường so sánh trong biểu thức LINQ

CHỈNH SỬA: Tôi chắc chắn muốn sử dụng ExpressionVisitor thay vì chỉ áp dụng một số tiện ích mở rộng tùy chỉnh hoặc thứ gì đó cho biểu thức của tôi khi được tạo ra vì một lý do quan trọng: Biểu thức được chuyển tới ExpressionVisitor của tôi được tạo bởi API Web ASP.Net Lớp ODATA, vì vậy tôi không có quyền kiểm soát cách nó được tạo ra (nghĩa là tôi không thể viết thường chuỗi mà nó đang tìm kiếm ngoại trừ từ bên trong ExpressionVisitor này).

Có hỗ trợ LINQ cho thực thể. Không chỉ mở rộng.

Đây là những gì tôi có cho đến nay. Nó tìm kiếm một cuộc gọi đến "Chứa" trên một chuỗi và sau đó gọi ToLower trên bất kỳ quyền truy cập thành viên nào bên trong biểu thức đó.

Tuy nhiên, nó không hoạt động. Nếu tôi xem các biểu thức sau khi thay đổi của tôi, nó có vẻ đúng với tôi, vì vậy tôi không chắc chắn những gì tôi có thể làm sai.

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); 
    } 

Nếu tôi đặt một breakpoint trên dòng "trở lại khái niệm" trong phương pháp VisitMember và sau đó làm một "ToString" trên "nút" và "khái niệm" các biến, các điểm break được nhấn hai lần, và đây là những gì hai bộ giá trị là:

Đầu tiên hit:

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

thứ hai hit:

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()" 

Tôi không biết đủ về các biểu thức để tìm ra những gì tôi đang làm sai tại thời điểm này. Bất kỳ ý tưởng?

+1

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

+1

Tránh 'ToLower' để so sánh chuỗi vì có nhiều khả năng dẫn đến lỗi ([Turkey Test] (http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html)). Hoặc sử dụng chữ hoa hoặc tốt hơn, như Corak đề nghị, String.Equals. – keyboardP

+0

Điều này sẽ không hoạt động trong trường hợp của tôi. Đầu tiên, tôi không có quyền kiểm soát Biểu thức, vì nó được tự động tạo bởi API Web ASP.Net. Thứ hai, tôi muốn một cái gì đó mà tôi có thể sử dụng chung để bọc một câu lệnh LINQ và sẽ làm việc với cả LINQ-to-entity lẫn LINQ-to-objects. –

Trả lời

2

tôi đã thực hiện một ứng dụng mẫu từ mã của bạn và nó có vẻ làm việc:

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

Hmm .... nó có thể có một cái gì đó để làm với biểu thức thực sự dài được thông qua vào chứa (lưu ý ở phần cuối của câu hỏi của tôi): "giá trị (System.Web.Http.OData.Query.Expressions.LinqParameterContainer + TypedLinqParameterContainer'1 [System.String]). TypedProperty ". Tôi không biết nó là gì, nhưng tôi vẫn áp dụng ToLower cho nó, vì nó là một kiểu String. –

+0

Hoặc nó có thể có một cái gì đó để làm với thực tế là LINQ to Entities đang thực hiện Expression? Nếu tôi cố gắng biên dịch/gọi trực tiếp biểu thức, tôi nhận được thông báo về "Phương thức này hỗ trợ cơ sở hạ tầng INQ to Entities và không có ý định sử dụng trực tiếp từ mã của bạn). –

+0

Xin lỗi, tôi không quen với OData. – Ben

0

bạn có thể tạo ra một phương pháp extesion như thế này:

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

Và sau đó bạn có thể gọi như thế này:

string teste = "teste"; 
string teste2 = "TESTE"; 

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

Cảm ơn, nhưng điều đó sẽ không hoạt động. Tôi muốn sử dụng một ExpressionVisitor vì vậy tôi có thể bọc LINQ của tôi tuyên bố như vậy mà tôi có thể sử dụng nó cả trong LINQ-to-đối tượng và LINQ-to-Entities. Tiện ích mở rộng tùy chỉnh InsensitiveEqual() sẽ không hoạt động trong LINQ-to-Entities. –

+0

Ngoài ra, tôi không có quyền kiểm soát việc tạo biểu thức, vì lớp ASP.Net Web API Odata đang tạo biểu thức. –

Các vấn đề liên quan