将整个函数作为 lambda 表达式传递给 LINQ

本文关键字:表达式 LINQ lambda 函数 | 更新日期: 2023-09-27 17:56:20

我从我的数据库中得到拍卖,如下所示:

var auctions = from o in db.auctions select o;

我想将此函数作为 lambda 表达式传递到 .我的 linq 子句过滤我的拍卖结果:

    private bool wordsHasProductName(auction a, string[] words)
    {
        if (!a.product_name.Contains(' ')) // Auction product name is single word - check if 
        {
            if (words.Length > 1) return false; // array is passed, single word is searching
            else return a.product_name.Contains(words[0]); // check if product name consists passed string
        }
        else // Auction product name has more words check if passed string is partially in this product name
        {
            string[] productName = a.product_name.Split(' ');
            var list = new List<string>(words);
            foreach (string item in productName) 
            {
                if (list.Contains(item)) list.Remove(item);
            }
            return list.Count == 0;
        }
    }

我在我的代码中调用它,在调试时,这一行没有错误,这是我调用的地方.linq 中的 Where() 子句:

string[] words = searchName.Split(' ');
auctions = auctions.Where((a) => wordsHasProductName(a, words));

但是在返回语句结束时,我收到异常错误:

return View(auctions.ToPagedList(pageNumber, pageSize));

错误代码:

类型为"系统.不支持的异常"发生在 EntityFramework.SqlServer.dll但未在用户代码中处理

其他信息:LINQ to 实体无法识别该方法 '布尔词HasProductName(IEP_Projekat.Models.auction, System.String[])' 方法,并且此方法不能转换为 存储表达式。

我怎样才能成功地将完整的布尔函数作为 linq 中的 lambda 表达式发送。哪里()?

将整个函数作为 lambda 表达式传递给 LINQ

正如其他人已经提到的,方法调用不能在 LINQ to Entities 中使用,因为它们不能转换为 SQL。因此,唯一可能的方法是将该方法重写为兼容表达式。另一个问题来了 - 您正在使用string.Split不支持的方法。

所以你不走运。或者可能不是。这是诀窍。您可以使用以下(IMO 等效)标准,而不是拆分product_name并检查它是否包含 words 中的每个单词:

words.All(word => (" " + product_name + " ").Contains(" " + word + " "))

用空格将product_nameword括起来可以让您在目标字符串的开头、中间或结尾匹配整个单词。它仅使用 EF 支持的构造。

把它们放在一起。该函数将如下所示:

private static Expression<Func<auction, bool>> wordsHasProductName(string[] words)
{
    if (words.Length == 1)
    {
        var word = words[0]; // Avoid ArrayIndex not supported
        return a => !a.product_name.Contains(" ") && a.product_name.Contains(word);
    }
    else
    {
        return a => words.All(word => (" " + a.product_name + " ").Contains(" " + word + " "));
    }
}

和用法:

string[] words = searchName.Split(' ');
auctions = auctions.Where(wordsHasProductName(words));

但是上面的实现并没有产生非常好的SQL查询,特别是对于多个单词。如果通过手动构建谓词表达式来重写它,则会产生更好的 SQL:

private static Expression<Func<auction, bool>> wordsHasProductName(string[] words)
{
    Expression<Func<auction, string>> product_name;
    Expression condition;
    var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    if (words.Length == 1)
    {
        // a => !a.product_name.Contains(" ") && a.product_name.Contains(words[0])
        product_name = a => a.product_name;
        condition = Expression.AndAlso(
            Expression.Not(Expression.Call(product_name.Body, containsMethod, Expression.Constant(" "))),
            Expression.Call(product_name.Body, containsMethod, Expression.Constant(words[0])));
    }
    else
    {
        // a => (" " + a.product_name + " ").Contains(" " + words[0] + " ")
        // && (" " + a.product_name + " ").Contains(" " + words[1] + " ")
        // ...
        // && (" " + a.product_name + " ").Contains(" " + words[N-1] + " ")
        product_name = a => " " + a.product_name + " ";
        condition = words
            .Select(word => Expression.Call(product_name.Body, containsMethod, Expression.Constant(" " + word + " ")))
            .Aggregate<Expression>(Expression.AndAlso);
    }
    return Expression.Lambda<Func<auction, bool>>(condition, product_name.Parameters);
}

欢迎来到 EF 和表达式的世界:)

不能将此复杂的 C# 逻辑作为可由 EF 提供程序解释的表达式树传递,提供程序最困难的部分是将其转换为 SQL 语句。

因此,您的选择是创建一个存储过程,在其中传入所需的参数,然后使用纯 SQL 编写逻辑。然后,将 SP 映射到 EF 并调用它以返回所需的对象列表。