如何为Like创建System.Linq.Expressions.Expression?

我从这个源创建了一个可过滤的BindingList。 它很棒:

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

应该做什么。 内部!=像解析器一样工作,将表达式==!=转换为System.Linq.Expressions.Expression 。 在这种情况下, ==成为System.Linq.Expressions.Expression.Equal

不幸的是System.Linq.Expressions.Expression不包含like运算符,我不知道如何解决这个问题。

初始代码如下所示:

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

然后我创建了一个能够做我想要的表达式:

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

例如

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

但是binaryOpFactory需要这样:

 Func 

预定义的表达式似乎正是这样:

 System.Linq.Expressions.Expression.Or; 

谁能告诉我如何转换我的表达?

就像是:

 static IEnumerable WhereLike( this IEnumerable 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>(body, param); return data.Where(lambda.Compile()); } static bool Like(string a, string b) { return a.Contains(b); // just for illustration } 

Func

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

我为IEnumerableIQueryable创建了2个扩展方法WhereFilter() 。 通过这种方式,您可以将此filter与例如Entity Framework一起使用,并且是在服务器上执行的过滤。

我使用了基于* (不是?)的filter,所以我可以使用底层的Linq方法StartsWith()EndsWith()Contains() 。 支持的格式:A *,* A,* A *,A * B.

用法:

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

这里是class级的基础知识:

 ///  /// Extension Methods for Filtering on IQueryable and IEnumerable ///  internal static class WhereFilterExtensions { ///  /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B ///  ///  /// Field to use for filtering. (Eg: item => item.Name) /// Filter: A*, *A, *A*, A*B /// Optional description of filter field used in error messages /// Filtered source public static IEnumerable WhereFilter(this IEnumerable source, Func 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; } ///  /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B ///  ///  /// Field to use for filtering. (Eg: item => item.Name)  /// Filter: A*, *A, *A*, A*B /// Optional description of filter field used in error messages /// Filtered source public static IQueryable WhereFilter(this IQueryable source, Expression> 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>( 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>( 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>( 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>( Expression.Call(selector.Body, "StartsWith", null, Expression.Constant(startsWith)), selector.Parameters[0] ) ).Where( Expression.Lambda>( Expression.Call(selector.Body, "EndsWith", null, Expression.Constant(endsWith)), selector.Parameters[0] ) ); } // XX if (astrixCount == 0 && filter.Length > 0) { return source.Where( Expression.Lambda>( 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; } }