无法在 EF Where 表达式中使用 LINQ for lambda 属性

Can't use LINQ in EF Where expression for lambda property

我这样修改了 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: The LINQ expression 'DbSet .Where(l => False || l.Active)' 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().

好吧,我 确实 调用了 AsListAsync() 并且我有点失望,因为这么简单的条件不是' .NET Core EF 无法解释。我怀疑我可能还遗漏了其他东西。

class东西看起来像这样。

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

检查 provided link 没有任何结果。

breaking changes in .Net core 3.0

一个解决方案(虽然不建议)如下:

If a query can't be fully translated, then either rewrite the query in a form that can be translated, or use AsEnumerable(), ToList(), or similar to explicitly bring data back to the client where it can then be further processed using LINQ-to-Objects.

因此,就像以前发生的那样,获取带有可翻译查询的数据库集,稍后使用您喜欢的任何 C# linq to Objects 魔法进行过滤。

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

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

问题出在 public 字段 Active 的表达式正文中。 EF 无法翻译此表达式 Active => DeletedOn == null && DeletedOn < DateTime.Now.

正如其他人所提到的,表达式 bodied 成员 getter 实际上被视为方法调用,它 由 Linq QueryProvider 转换为原生 SQL,因此您需要再次撤消您的 'DRY' 代码,而是将 Active 属性 中的谓词逻辑直接扩展到您的 Linq 查询中,或者您需要将整个表达式树或 IQueryable 传递给类似的方法Active 这样它就可以将谓词组合到其中(这似乎有点过分了)。

此外,根据评论,存在一个逻辑问题 - _.DeletedOn == null && _.Deletedon < DateTime.Now 将 return 为假,这意味着查询将根据 all 参数的值全有或全无。据推测,您正在寻找一个有条件应用的 'logical delete' 过滤器,它还允许将来删除日期,即任何尚未删除的记录都被视为活动的,或者任何具有null 删除日期 根本没有删除

最后,对于大多数 RDBMS,尤其是 SQL 服务器,将硬编码谓词(如 all)添加到 WHERE 过滤器中通常不是一个好主意,因为这可能会像下面这样扰乱 query plan caching. In your case, the all flag filtering logic simply decides whether you want to include logically Deleted data or not. IMO, I would instead 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;

尝试在过滤记录之前插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用,即在本例中,在 Where 条件之前。