IQueryable Extension:创建lambda表达式以查询关键字的列

我从CodePlex上的这个例子开始使用IQueryable扩展方法。

我认为我需要的是一个IQueryable扩展方法到“Where”,方法签名如下所示:

public static IQueryable Where(this IQueryable source, string columnName, string keyword) 

并有效地做到这一点(假设T.columnName是字符串类型):

 source.Where(p => p.ColumnName.Contains("keyword")) 

使用上面的CodePlex示例,我想我理解他如何使OrderBy方法工作,但我的问题似乎有点复杂,我不知道如何让Contains(“关键字”)部分工作。

提前致谢,

–ed

更新:9/13/2010太平洋标准时间下午6:26

我认为以下内容可行,但最终得到一个NotSupportedException(当LINQ to Entities时,LINQ表达式节点类型’Invoke’不受支持。)当我通过Count()执行表达式时。 有任何想法吗?

  public static IQueryable Where(this IQueryable source, string columnName, string keyword) { var type = typeof(T); var property = type.GetProperty(columnName); if (property.PropertyType == typeof(string)) { var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var sel = Expression.Lambda<Func>(propertyAccess, parameter); var compiledSel = sel.Compile(); return source.Where(item => compiledSel(item).Contains(keyword)); } else { return source; } } 

 public static IQueryable Where( this IQueryable source, string columnName, string keyword) { var arg = Expression.Parameter(typeof(T), "p"); var body = Expression.Call( Expression.Property(arg, columnName), "Contains", null, Expression.Constant(keyword)); var predicate = Expression.Lambda>(body, arg); return source.Where(predicate); } 

好几年后,但如果还有人需要这个,那么它是:

  public static IQueryable Has(this IQueryable source, string propertyName, string keyword) { if (source == null || propertyName.IsNull() || keyword.IsNull()) { return source; } keyword = keyword.ToLower(); var parameter = Expression.Parameter(source.ElementType, String.Empty); var property = Expression.Property(parameter, propertyName); var CONTAINS_METHOD = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var TO_LOWER_METHOD = typeof(string).GetMethod("ToLower", new Type[] { }); var toLowerExpression = Expression.Call(property, TO_LOWER_METHOD); var termConstant = Expression.Constant(keyword, typeof(string)); var containsExpression = Expression.Call(toLowerExpression, CONTAINS_METHOD, termConstant); var predicate = Expression.Lambda>(containsExpression, parameter); var methodCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { source.ElementType }, source.Expression, Expression.Quote(predicate)); return source.Provider.CreateQuery(methodCallExpression); } 

我打电话给我的方法“Has”只是为了保持简短,样本用法:

 filtered = filtered.AsQueryable().Has("City", strCity) 

并且您可以连接以使其更具表现力:

 filtered = filtered.AsQueryable().Has("Name", strName).Has("City", strCity).Has("State", strState); 

顺便说一句,附加到字符串的“IsNull()”只是另一种简单的扩展方法:

  public static Boolean IsNull(this string str) { return string.IsNullOrEmpty(str); } 

.Contains("keyword")部分在您的示例中完全正确。

这是p.ColumnName部分会导致麻烦。

现在,有很多方法可以做到这一点,通常涉及reflection或Expression<> ,两者都不是特别有效。

这里的问题是通过将列名称作为字符串传递,您正在撤消LINQ发明允许的确切事物。

但是,除此之外,可能有更好的方法来完成您的整体任务。

那么,让我们看看其他方式:

你希望能够说:

  var selector = new Selector("Column1", "keyword"); mylist.Where(item => selector(item)); 

并且它相当于

  mylist.Where(item=> item.Column1.Contains("keyword")); 

我们怎么样:

  Func selector = i => i.Column1; mylist.Where(item => selector(item).Contains("keyword")); 

要么

  Func selector = i => i.Column1.Contains("keyword"); mylist.Where(item => selector(item)); 

这些很容易扩展为替代品:

  Func selector; if (option == 1) selector = i => i.Column1; else selector = i => i.Column2; mylist.Where(item => selector(item).Contains("keyword")); 

请参阅,在generics类型的对象动态工作。 因此,当p.ColumnName被视为字符串时,已执行字符串的包含。

通常,对于您指定的任何lambda表达式,它会将事物解析为Expression,并最终在运行时生成输出。

如果您看到我的post: http : //www.abhisheksur.com/2010/09/use-of-expression-trees-in-lamda-c.html,您将了解它的实际工作原理。