【发布时间】:2015-07-10 14:39:00
【问题描述】:
如何为这样的实体框架创建属性选择器?
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList();
}
我希望调用代码能够像这样干净简单:
var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query);
我收到“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'。”错误。我无法弄清楚如何构建表达式。
更新:
根据 MBoros 的回答,这是我最终得到的代码。效果很好。
表达式树的关键是理解表达式树就是将你通常在代码中编写的内容(如“e => e.Username.IndexOf(query)”)分解为一系列对象:“e”得到它自己的对象,“用户名”它自己的对象,“IndexOf()”它自己的对象,“查询”常量它自己的对象,等等。第二个关键是要知道你可以使用Expression类上的一系列静态方法来创建各种这些对象,如下所示。
PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member;
ParameterExpression parameter = Expression.Parameter(typeof(T), "e");
MemberExpression accessor = Expression.Property(parameter, pinfo);
ConstantExpression queryString = Expression.Constant(query, typeof(string));
ConstantExpression minusOne = Expression.Constant(-1, typeof(int));
MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this
Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString);
Expression expression = Expression.GreaterThan(indexOf, minusOne);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter);
//return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want.
var results = queryable.Where(predicate).ToList();
return results;
现在我有一个真正的问题,但我会在一个单独的问题中提出。我的真实查询如下所示:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e } ).ToList();
}
所以我需要构建一个返回匿名类型的表达式!!或者即使我创建了一个类来提供帮助,我也需要编写一个返回新对象的表达式。但我会将其包含在一个单独的问题中。
【问题讨论】:
-
您可能需要查看PredicateBuilder 来帮助您完成此操作
-
之前发布答案的人。那行得通!感谢神秘人删除了你的帖子。
-
我删除了我的答案,因为它实际上并没有编译为 SQL,它只是在内存中。我正在研究如何使其正确编译为 sql。回来看看,希望我能得到一个有效的答案。
-
我还没有找到一种能够在多个属性上重用
ToLower().Contains()逻辑的好方法。我确信有一种方法可以通过构建表达式来做到这一点,我还没有足够的使用表达式的经验来弄清楚它。我会谨慎使用已删除的答案,因为它不会编译为 SQL(据我所知),这意味着您将把整个表拉下来,然后过滤它,这不是您想要的去做。
标签: c# linq entity-framework expression-trees