c# - 如何在 ef6 中构建简单的属性选择器表达式

标签 c# linq entity-framework expression-trees

如何为这样的 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);

我收到“LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。”错误。我无法弄清楚如何构建表达式。

更新:

根据 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 委托(delegate)。但是您可以将属性选择器作为表达式树传递,因此您的签名将是:

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()(使用 .Contains 而不是 .IndexOf()> 1)。这其实很棘手。通常数据库使用其默认排序规则,因此如果将其设置为 CI(不区分大小写),那么它将以这种方式进行比较。如果您没有任何限制,并且可以调整数据库排序规则,我会这样做。在这种情况下,您可以省略 .ToLower() 调用。 否则请查看此答案:https://stackoverflow.com/a/2433217/280562

关于c# - 如何在 ef6 中构建简单的属性选择器表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31343603/

相关文章:

WPF/MVVM/EF - 如何绑定(bind)到实体的相关实体?

c# - 代码优先更改桥实体表的名称

c# - ASP.NET MVC 自定义多字段验证

c# - angularjs传递数据有列表数据

c# - 林克异常 : Function can only be invoked from linq to entities

c# - 在 C# 中使用 LINQ 减少字典中的重复项

c# - Entity Framework Database First 是否知道数据库中设置的唯一键?

c# - 无法启动 Azure 辅助角色,异常代码 0xe0434352 和 0xC0000035

c# - 从窗口内容截屏(无边框)

c# - 如何拆分多个级别的成员访问权限