如何在ef6中构建一个简单的属性选择器表达式
本文关键字:简单 一个 属性 表达式 选择器 ef6 构建 | 更新日期: 2023-09-27 18:02: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表达式节点类型'Invoke'在LINQ到实体中不受支持。"错误。我想不出该如何表达。
更新:基于MBoros的回答,这里是我最终得到的代码。
表达式树的关键是理解表达式树是关于将你通常在代码中写的东西(如"e => e.Username.IndexOf(query)")分解成一系列对象:"e"有它自己的对象,"Username"有它自己的对象,"IndexOf()"有它自己的对象,"query"常数有它自己的对象,等等。第二个关键是要知道,您可以在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();
}
所以我需要构建一个返回匿名类型的表达式!!或者,即使我创建了一个类来提供帮助,我也需要编写一个返回新对象的表达式。但是我将把这个放在一个单独的问题中。
不能简单地在sql中调用CLR委托。但是您可以将属性选择器作为表达式树传入。,所以你的签名应该是:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)
调用看起来是一样的。但是现在你手上有了一个表达,你可以看看这个答案:将表达式形参作为参数传递给另一个表达式它为您提供了简单地将表达式树放入另一个表达式树中的工具。在您的例子中,它看起来像:
Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();
一旦你在那里,你仍然有。tolower (). contains()调用(使用。contains而不是。indexof()> 1)。这实际上是棘手的。通常,数据库使用其默认排序规则,所以如果它设置为CI(不区分大小写),那么它将以这种方式进行比较。如果您没有任何约束,并且可以调整数据库排序,我会这样做。在这种情况下,可以省略. tolower()调用。否则,请查看以下答案:https://stackoverflow.com/a/2433217/280562