我这样更改了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并没有给我任何帮助。
答案 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 条件之前。