不能在EF Where中使用LINQ for lambda属性

时间:2020-01-02 09:02:23

标签: c# .net-core .net-core-3.0

我这样更改了LINQ表达式。

public async Task<IEnumerable<Thing>> Get(bool all)
{
  List<Thing> output = await Context.Things
    //.Where(_ => all || _.DeletedOn == null && _.Deletedon < DateTime.Now)
    .Where(_ => all || _.Active)
    .ToListAsync();
  return output;
}

显然,它导致了以下错误。

InvalidOperationException:无法转换LINQ表达式'DbSet .Where(l => False || l.Active)'。以一种可以翻译的形式重写查询,或者通过插入对AsEnumerable(),AsAsyncEnumerable(),ToList()或ToListAsync()的调用来显式切换到客户端评估。

好吧,我确实有调用了 AsListAsync(),但我感到有些困惑,因为.NET Core EF无法解释这种简单的条件。我怀疑我可能会想念其他东西。

事物看起来像这样。

class Thing
{
  ...
  public DateTime? DeletedOn { get; set; }
  public Active => DeletedOn == null && DeletedOn < DateTime.Now.
}

检查provided link并没有给我任何帮助。

4 个答案:

答案 0 :(得分:4)

正如其他人所提到的,表达式身体强壮的成员getter实际上被视为一个方法调用,Linq QueryProvider将其can't be parsed转换为本机SQL,因此您需要再次撤消您的'DRY'代码,然后将Active属性中的谓词逻辑直接扩展到您的Linq查询中,或者,您需要将整个Expression Tree或IQueryable传递给类似Active的方法,以便它可以组成谓词进去(这似乎太过分了)。

此外,根据注释,还有一个逻辑问题-_.DeletedOn == null && _.Deletedon < DateTime.Now将返回false,这意味着基于all参数的值,查询将全部或全部为空。猜测中,您正在寻找有条件应用的“逻辑删除”过滤器,该过滤器还允许将来进行带日期的删除,即,尚未删除的任何记录都将被视为活动记录,或者具有空值的任何记录删除日期根本没有被删除。

最后,对于大多数RDBMS,尤其是SQL Server,将硬编码谓词(如all)添加到WHERE过滤器中通常不是一个好主意,因为这可能会干扰query plan caching。就您而言,all标志过滤逻辑仅决定您是否要逻辑上包含Deleted数据。 IMO,我将改为conditionally compose the query,以避免在SQL中进行不必要的过滤。

将所有内容放在一起,查询将是:

var query = Context.Things;
if (!all)
{
   query = query.Where(t => t.DeletedOn == null || t.Deletedon > DateTime.Now)
}
var output = await query
   .ToListAsync();
return output;

答案 1 :(得分:2)

请参见breaking changes in .Net core 3.0

以下一种解决方案(尽管不建议使用):

如果查询无法完全翻译,则以可以翻译的形式重写查询,或者使用AsEnumerable(),ToList()或类似方法将数据显式带回客户端,然后使用LINQ-to-Objects进行进一步处理。

因此,就像以前发生的那样,请使用可翻译的查询获取dbset,然后再使用所需的C#linq to Objects魔术进行过滤。

未经测试,我相信EF无法翻译您的Expression bodied member。尝试以下操作是否有效?

   .Where(_ => all || _.DeletedOn == null && _.Deletedon < DateTime.Now) 

答案 2 :(得分:1)

问题出在公共领域Active中的表达式主体中。 EF无法翻译此表达式Active => DeletedOn == null && DeletedOn < DateTime.Now

答案 3 :(得分:0)

尝试在过滤记录之前插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用,即在这种情况下,在 Where 条件之前。