如何在ef6中构建一个简单的属性选择器表达式

时间:2015-07-10 14:39:01

标签: c# linq entity-framework expression-trees

如何为此类实体框架创建属性选择器?

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);

我得到一个&#34; LINQ表达式节点类型&#39; Invoke&#39; LINQ to Entities不支持。&#34;错误。我无法弄清楚如何构建表达式。

更新:

基于MBoros的答案,这里是我最终得到的代码。它很棒。

表达式树的关键是理解表达式树是关于将你通常在代码中编写的内容(例如&#34; e =&gt; e.Username.IndexOf(query)&#34;)分解成一系列对象:&#34; e&#34;获得自己的对象,&#34;用户名&#34;它自己的对象,&#34; IndexOf()&#34;它自己的对象,&#34;查询&#34;恒定自己的对象,等等。第二个关键是要知道你可以在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();

}

所以我需要构建一个返回匿名类型的表达式!或者即使我创建了一个帮助的类,我也需要编写一个返回一个新对象的表达式。但我会在另一个问题中加入这个问题。

1 个答案:

答案 0 :(得分:2)

您无法在sql中调用CLR委托。但是您可以将属性选择器作为表达式树传递。,因此您的签名将是:

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)

通话看起来一样。但既然你掌握了一个表达,你可以看看这个答案: Pass expression parameter as argument to another expression 它为您提供了简单地将表达式树放在另一个表达式中的工具。在你的情况下,它看起来像:

Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();

一旦你在那里,你仍然有.ToLower()。包含()调用(使用.Contains而不是.IndexOf()&gt; 1)。这实际上很棘手。通常,db使用其默认排序规则,因此如果它设置为CI(不区分大小写),那么它将以这种方式进行比较。如果你没有任何约束,并且可以调整db排序规则,我会那样做。在这种情况下,您可以省略.ToLower()调用。 否则请查看此anser:https://stackoverflow.com/a/2433217/280562