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,您将了解它的实际工作原理。
- 部署网站时缺少using指令或程序集引用错误
- 无法加载{myassembly}导入的过程
- WPF将项添加到绑定到observablecollectionexception的datagrid
- 我什么时候应该使用公共/私有/静态方法?
- ServiceStack.OrmLite:编写自定义SQL并获取结果集的方法在哪里?
- 无法在web api控制器中遇到断点
- 如何设置图表栏的宽度?
- 克隆Linq对象错误“类型’TestLinq.PersonAddress’的对象图包含循环,如果禁用参考跟踪,则无法序列化。”
- PresentationFramework.dll中出现未处理的“System.Windows.Markup.XamlParseException”类型exception