【问题标题】:The LINQ expression could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation无法翻译 LINQ 表达式。要么以可翻译的形式重写查询,要么切换到客户评估
【发布时间】:2021-10-14 15:49:41
【问题描述】:

我有 C# 应用程序 (.NET Core 3.1),并且我编写了以下 LINQ 表达式。

public ActionResult<bool> GetItems(string title)
{
     var items = _service.All.GetItems().OrderByDescending(o => o.Id).Where(w => w.Portal_Id == 1);

     if (!string.IsNullOrWhiteSpace(title))
     {
            var terms = title.Split(' ').ToList();
            items = items.Where(w => terms.Any(a => w.ItemName.Contains(a)));
     }
     // Some Other code
     return Ok();
}

每当执行此表达式时,我都会收到以下错误

The LINQ expression 'DbSet<PosItem>\r\n    .Where(p => !(p.IsDeleted))\r\n    
.OrderByDescending(p => p.CreatedAt)\r\n    .Where(p => p.Portal_Id == 1)\r\n    .Where(p => __terms_1\r\n      
.Any(a => p.ItemName.Contains(a)))' could not be translated.

Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by 
inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
See https://go.microsoft.com/fwlink/?linkid=2101038 for more information." 

我无法添加 ToList() 并切换到客户端评估,因为数据集太大而无法这样做。

请告知如何在不切换到客户评估的情况下解决此问题。

谢谢

【问题讨论】:

  • 某些函数调用可以根据这个table翻译成SQL。在您的特定情况下并且没有任何编码,我认为您需要的问题是items.Where(w =&gt; terms.Any(a =&gt; w.ItemName.Contains(a)))。您正在测试一个客户端数组。
  • 可能无法翻译terms.Any()。您可以使用ExpressionBuilder 和for/foreach 创建Expression,然后在items.Where(your_new_expression) 中使用新创建的Expression
  • 这个答案有一个例子告诉你如何做到这一点How to create an Expression builder in .NET
  • 很可能它不起作用,因为 Contains 方法
  • 您提到数据很大,因此您希望避免进行任何客户端处理。正如这里多处所指出的,使用Contains() 将在最终的SQL 查询中产生一个或多个LIKE '%item%' 术语。反过来,这可以很容易地在服务器上作为table scan 执行。这可能值得,也可能不值得。

标签: c# entity-framework linq .net-core


【解决方案1】:

问题是您试图在Any 表达式中执行string.Contains,EF 将在尝试组合成SQL 时窒息。 Ceptus 在鼻子上,为Where 子句构建谓词 OR-ing 术语比较。否则,您的代码应该在没有包含检查的情况下工作,而是进行相等检查:

没有Contains:(平等检查而不是LIKE %name%

var terms = title.Split(' ').ToList();
items = items.Where(w => terms.Contains(w.ItemName)); // IN clause.

内置表达式:

var terms = title.Split(' ').ToList();
Expression<Func<Item, bool>> predicate = (Item) => false;
foreach(var term in terms)
    predicate = predicate.Or(x => x.ItemName.Contains(term));

items = items.Where(predicate);

因此,对于标题中的每个术语,我们在 ItemName 上使用 LIKE %term% 进行 OR 匹配。

【讨论】:

  • 感谢您的宝贵时间和建议。我对您的解决方案进行了一些更改,并使用了 var predicate = PredicateBuilder.New(false);我工作了
【解决方案2】:

在一个表达式中使用多个 ParameterExpression 时也会出现此问题。始终将 den ParameterExpression 实例传递给所有 Expression.Property(argPara, "Name") 值。

【讨论】:

  • 与问题无关:他们没有在这里构建表达式。
猜你喜欢
  • 1970-01-01
  • 2020-06-16
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 2020-05-21
  • 2021-11-22
相关资源
最近更新 更多