2009-06-05 15 views
14

Tôi đã tạo BindingList from this source có thể lọc. Nó hoạt động tuyệt vời:Cách tạo System.Linq.Expressions.Expression cho Thích?

list.Filter("Customer == 'Name'"); 

làm những gì cần. Nội bộ hoạt động giống như trình phân tích cú pháp, chuyển đổi biểu thức == hoặc != thành System.Linq.Expressions.Expression. Trong trường hợp này, == sẽ trở thành System.Linq.Expressions.Expression.Equal.

Thật không may System.Linq.Expressions.Expression không chứa nhà điều hành tương tự và tôi không biết cách giải quyết vấn đề này.

Mã ban đầu trông như thế này:

private static Dictionary<String, Func<Expression, Expression, Expression>> 
    binaryOpFactory = new Dictionary<String, Func<Expression, Expression, Expression>>(); 

static Init() { 
    binaryOpFactory.Add("==", Expression.Equal); 
    binaryOpFactory.Add(">", Expression.GreaterThan); 
    binaryOpFactory.Add("<", Expression.LessThan); 
    binaryOpFactory.Add(">=", Expression.GreaterThanOrEqual); 
    binaryOpFactory.Add("<=", Expression.LessThanOrEqual); 
    binaryOpFactory.Add("!=", Expression.NotEqual); 
    binaryOpFactory.Add("&&", Expression.And); 
    binaryOpFactory.Add("||", Expression.Or); 
} 

Sau đó, tôi tạo ra một biểu thức mà sẽ làm những gì tôi muốn:

private static System.Linq.Expressions.Expression<Func<String, String, bool>> 
    Like_Lambda = (item, search) => item.ToLower().Contains(search.ToLower()); 

private static Func<String, String, bool> Like = Like_Lambda.Compile(); 

ví dụ

Console.WriteLine(like("McDonalds", "donAld")); // true 
Console.WriteLine(like("McDonalds", "King")); // false 

Nhưng binaryOpFactory đòi hỏi này:

Func<Expression, Expression, Expression> 

Các biểu thức được xác định trước dường như là chính xác rằng:

System.Linq.Expressions.Expression.Or; 

bất cứ ai có thể cho tôi biết làm thế nào để chuyển đổi biểu hiện của tôi không?

+0

Và như thế nào NHƯ của bạn hoạt động? Tôi có thể giúp bạn xây dựng một biểu thức, nhưng tôi cần phải hiểu làm thế nào bạn muốn nó làm việc đầu tiên ... regex? chứa đựng? v.v. –

+0

Điều đó không quan trọng. Việc thực hiện cuối cùng sẽ được thuận lợi với regexp. Về cơ bản tôi có một Func để tôi vượt qua 2 Strings và nhận được true hoặc false như returnvalue. Vấn đề của tôi là tôi không hiểu việc thực hiện các đối tượng trong System.Linq.Expressions.Expression Namespace, mà dường như là Func (xem các loại argements chung của binaryOpFactory) vì vậy tôi không thể tạo so sánh của riêng tôi. –

+0

Hãy bình luận: hiểu được Expression API có thể làm một số việc ... Tôi cố gắng trình bày một vài điều cơ bản trên blog của mình; Cuốn sách của Jon (C# in Depth) cũng đưa ra một cái nhìn tổng quan ở mức cao. –

Trả lời

15

Cái gì như:

static IEnumerable<T> WhereLike<T>(
     this IEnumerable<T> data, 
     string propertyOrFieldName, 
     string value) 
{ 
    var param = Expression.Parameter(typeof(T), "x"); 
    var body = Expression.Call(
     typeof(Program).GetMethod("Like", 
      BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public), 
      Expression.PropertyOrField(param, propertyOrFieldName), 
      Expression.Constant(value, typeof(string))); 
    var lambda = Expression.Lambda<Func<T, bool>>(body, param); 
    return data.Where(lambda.Compile()); 
} 
static bool Like(string a, string b) { 
    return a.Contains(b); // just for illustration 
} 

Trong điều khoản của một Func<Expression,Expression,Expression>:

static Expression Like(Expression lhs, Expression rhs) 
{ 
    return Expression.Call(
     typeof(Program).GetMethod("Like", 
      BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) 
      ,lhs,rhs); 
} 
+0

trông đẹp mắt, nhưng tôi cần sth. trả về một biểu thức nhưng câu hỏi là, expression1, expression2 và expression3 là gì trong ngữ cảnh này? Một ví dụ làm thế nào Expression.Equal làm việc trong nội bộ whould được tốt đẹp. –

+0

Tôi phải thừa nhận, tôi không hiểu toàn bộ ma thuật đằng sau mã, nhưng mã pice thứ hai hoạt động như một sự quyến rũ. –

+0

@Marc Tính năng này có hoạt động với LINQ to Entities không? –

3

Tôi tạo ra 2 phương pháp khuyến nông WhereFilter() cho IEnumerableIQueryable. Bằng cách này, bạn cũng có thể sử dụng bộ lọc này với v.v. Entity Framework và là bộ lọc được thực hiện trên máy chủ.

tôi đã sử dụng một bộ lọc dựa trên * (không?) Vì vậy tôi có thể sử dụng phương pháp underlaying LINQ StartsWith(), EndsWith()Contains(). các định dạng được hỗ trợ: A *, * A, * A *, A * B

Cách sử dụng:

var filtered = list.WhereFilter(i => i.Name, "a*", "First Name"); 

đây những điều cơ bản của lớp:

/// <summary> 
/// Extension Methods for Filtering on IQueryable and IEnumerable 
/// </summary> 
internal static class WhereFilterExtensions 
{ 
    /// <summary> 
    /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="selector">Field to use for filtering. (E.g: item => item.Name)</param> 
    /// <param name="filter">Filter: A*, *A, *A*, A*B</param> 
    /// <param name="fieldName">Optional description of filter field used in error messages</param> 
    /// <returns>Filtered source</returns> 
    public static IEnumerable<T> WhereFilter<T>(this IEnumerable<T> source, Func<T, string> selector, string filter, string fieldName) 
    { 

     if (filter == null) 
      return source; 

     if (selector == null) 
      return source; 

     int astrixCount = filter.Count(c => c.Equals('*')); 
     if (astrixCount > 2) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     if (filter.Contains("?")) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'")); 


     // *XX* 
     if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).Contains(filter)); 
     } 

     // *XX 
     if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).EndsWith(filter)); 
     } 

     // XX* 
     if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).StartsWith(filter)); 
     } 

     // X*X 
     if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*")) 
     { 
      string startsWith = filter.Substring(0, filter.IndexOf('*')); 
      string endsWith = filter.Substring(filter.IndexOf('*') + 1); 

      return source.Where(item => selector.Invoke(item).StartsWith(startsWith) && selector.Invoke(item).EndsWith(endsWith)); 
     } 

     // XX 
     if (astrixCount == 0 && filter.Length > 0) 
     { 
      return source.Where(item => selector.Invoke(item).Equals(filter)); 
     } 

     // * 
     if (astrixCount == 1 && filter.Length == 1) 
      return source; 

     // Invalid Filter 
     if (astrixCount > 0)    
      throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // Empty string: all results 
     return source; 


    } 

    /// <summary> 
    /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="selector">Field to use for filtering. (E.g: item => item.Name)  </param> 
    /// <param name="filter">Filter: A*, *A, *A*, A*B</param> 
    /// <param name="fieldName">Optional description of filter field used in error messages</param> 
    /// <returns>Filtered source</returns> 
    public static IQueryable<T> WhereFilter<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string filter, string fieldName) 
    { 

     if (filter == null) 
      return source; 

     if (selector == null) 
      return source; 

     int astrixCount = filter.Count(c => c.Equals('*')); 
     if (astrixCount > 2) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName==null?"":" for '" + fieldName + "'")); 

     if (filter.Contains("?"))    
      throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // *XX* 
     if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") &&   filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "Contains", null, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // *XX 
     if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "EndsWith", null, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // XX* 
     if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "StartsWith", null,   Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // X*X 
     if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*")) 
     { 
      string startsWith = filter.Substring(0, filter.IndexOf('*')); 
      string endsWith = filter.Substring(filter.IndexOf('*') + 1); 

      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "StartsWith", null,   Expression.Constant(startsWith)), 
        selector.Parameters[0] 
       ) 
      ).Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "EndsWith", null,   Expression.Constant(endsWith)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // XX 
     if (astrixCount == 0 && filter.Length > 0) 
     { 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Equal(selector.Body, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // * 
     if (astrixCount == 1 && filter.Length == 1) 
      return source; 

     // Invalid Filter 
     if (astrixCount > 0) 
      throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // Empty string: all results 
     return source; 

    } 
} 
+0

bởi 'với ví dụ: Entity Framework và là bộ lọc được thực hiện trên máy chủ 'bạn có nghĩa là ở cơ sở dữ liệu - tức là: dịch sang SQL? Tôi đã bị ấn tượng bởi 'selector.Invoke' ngăn EF dịch sang SQL? – JoeBrockhaus

+0

Có, ExpressionTree được chuyển đổi bởi 'Nhà cung cấp LINQ'. Khi sử dụng Enity Framework, nhà cung cấp LINQ to Entities sẽ chuyển đổi nó thành một chuỗi SQL sẽ được thực hiện trên cơ sở dữ liệu. Kết quả sẽ không có Array mà là một IEnumerable đi qua DataReader. –

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