2009-06-05 14 views
14

Filtrelenebilir BindingList from this source oluşturdu. Bu büyük çalışır:Nasıl Like için bir System.Linq.Expressions.Expression oluşturulur?

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

ne olması gerektiği yapar. Dahili, == veya != ifadesini System.Linq.Expressions.Expression içine dönüştüren bir ayrıştırıcı gibi çalışır. Bu durumda, ==, System.Linq.Expressions.Expression.Equal olur.

System.Linq.Expressions.Expression, benzer bir işleç içermiyor ve bunu nasıl çözeceğimi bilmiyorum.

ilk kod şuna benzer:

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

örn:

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

Sonra ben istediğimi yapacağım bir ifade yarattı

Func<Expression, Expression, Expression> 

önceden tanımlanmış ifadeleri

tam olarak bu gibi görünüyor:

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

Ama binaryOpFactory bunu gerektirir

System.Linq.Expressions.Expression.Or; 

kimse nasıl benim ifadesini dönüştürmek için bana söyleyebilir misiniz? gibi

+0

Ve nasıl GİBİ çalışır? Bir İfadeyi oluşturmana yardım edebilirim, ama önce nasıl çalışmasını istediğimi anlamalıyım ... regex? içeriyor? vb? –

+0

Bu önemli değil. Nihai uygulama, regexp ile mümkün olacaktır. Temel olarak, bir Func 'a 2 Strings'i geçtim ve getiri değeri olarak doğru ya da yanlış aldım. Benim Sorunum, System.Linq.Expressions.Expression Ad Alanı içindeki Object'lerin uygulanmasını anlamam, çünkü Func <İfade, İfade, İfade> gibi görünüyor (binaryOpFactory genel tür argümanlarına bakın). kendi karşılaştırmam. –

+0

Yorum yap: İfade API'sinin anlaşılması biraz zaman alabilir ... Blogumda birkaç temel bilgiyi incelemeye çalışıyorum; Jon'un kitabı (Derinlik C#) ayrıca yüksek düzeyde bir genel bakış sunar. –

cevap

15

şey: Bir Func<Expression,Expression,Expression> açısından

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 
} 

:

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

güzel görünüyor, ama sth'ye ihtiyacım var. Bu bir Func <İfade, İfade, İfade> döndürür. Ama soru şu, bu bağlamda expression1, expression2 ve expression3 nedir? Expression.Equal'in nasıl çalıştığı güzel bir örnek. –

+0

Kabul etmeliyim, kodun arkasındaki tüm sihri anlayamıyorum, ancak kodun ikinci katı bir çekicilik gibi çalışır. –

+0

@Marc Bu, LINQ'den Entity ile çalışır mı? –

3

Ben IEnumerable ve IQueryable için 2 uzatma yöntemleri WhereFilter() yarattı. Bu şekilde, bu filtreyi örn. Varlık Çerçevesi ve sunucu üzerinde gerçekleştirilen filtrelemedir.

ben dayalı bir filtre kullanılır * (değil?) Bu yüzden underlaying Linq yöntemleri StartsWith(), EndsWith() ve Contains() kullanabilirsiniz. Desteklenen formatlar: A *, * A, * A * A * B

Kullanımı:

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

sınıfın Temelleri:

/// <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

'ile, ör. Varlık Çerçevesi ve sunucu üzerinde gerçekleştirilen filtreleme 'veritabanında - yani: SQL'e çevriliyor mu? “Selector.Invoke”, EF'in SQL'e çevirisi yapmasını engellediği izleniminin altındaydı. – JoeBrockhaus

+0

Evet, ExpressionTree, 'LINQ Sağlayıcısı' tarafından dönüştürülür. Enity Framework kullanırken, LINQ - Varlıklar sağlayıcısı, veritabanında çalıştırılacak bir SQL dizesine dönüştürür. Sonuç, Array olmayacak ancak DataReader'da dolaşan bir IEnumerable olmayacaktır. –

İlgili konular