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

How to build a simple property selector expression in ef6

如何为 entity framework 创建一个 属性 选择器?

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

我收到 "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." 错误。我不知道如何构建表达式。

更新:

根据 MBoros 的回答,这里是我最终得到的代码。效果很好。

表达式树的关键是理解表达式树都是关于将您通常在代码中编写的内容(如 "e => e.Username.IndexOf(query)")分解为一系列对象:"e" 获得自己的对象, "Username" 它自己的对象,"IndexOf()" 它自己的对象,"query" 常量它自己的对象,等等。第二个关键是要知道你可以使用Expressionclass上的一系列静态方法来创建各种这些对象,如下所示。

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

}

所以我需要构建一个 returns 匿名类型的表达式!!或者即使我创建了一个 class 来帮助,我也需要写一个表达式来表示 returns 一个新的对象。但我会把它包括在一个单独的问题中。

您不能在 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() 调用。 否则请查看此答案: