Ef core 3 强制转换无法被翻译。

4
我想把我的IQueryable转换成一个接口,类似于这样:
public static IQueryable<T> _enableFilter<T>(this IQueryable<T> queryable) => queryable.Where(x => (x as IEnable).Enable);

_newsRepository.BaseQuery.EnableFilter().FirstOrDefaultAsync(x => x.Id == model.Id);

它可以在EF Core 2.2上运行,但在3上会出现此错误:
System.InvalidOperationException : The LINQ expression 'Where<News>(
    source: DbSet<News>, 
    predicate: (n) => (n as IEnable).Enable)' 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.


2
它在EF Core 2.2中也不起作用。在SQL中,该转换没有意义 - SQL中没有接口或继承。EF Core 2.2会静默地使用客户端评估进行转换 - 它将所有数据拉到客户端并在那里过滤结果。显然,这比适当的SQL查询要慢得多。 - Panagiotis Kanavos
1
你为什么要进行接口转换呢?如果需要创建一个可以在IEnable派生类上工作的通用方法,请添加类型约束,即> _enableFilter<T> where T:IEnable - Panagiotis Kanavos
1
感谢您添加“FirstOrDefault”位。请查看此处:https://github.com/aspnet/EntityFrameworkCore/issues/11092 - paulsm4
谢谢您的回答。是的,没问题,但我该如何将我的模型转换为我的接口?您有什么解决方案吗? - Mo3in
@Mo3in 我已经解释了你可以做什么 - 添加类型约束,这样你就不必使用那个转换了。我不确定 EF 是否会接受它,但你肯定会摆脱转换。 - Panagiotis Kanavos
@Mo3in,你是在尝试实现软删除吗?如果是的话,全局查询过滤器是更好的选择。 - Panagiotis Kanavos
2个回答

6

这个查询在EF Core 2.2中也不起作用。在SQL中,这种转换没有任何意义 - SQL中没有接口或继承。在EF Core 3之前,有一个不幸的特性,即客户端评估。如果EF Core无法将某些内容翻译成SQL,则会将所有数据拉到客户端并尝试在那里过滤数据。不用说,这对性能是灾难性的。更糟糕的是,在没有任何警告的情况下进行评估。警告或异常可以让您识别和解决问题。至少,在EF Core 2.2中,可以禁用客户端评估。在EF Core 3中,它彻底消失了。

至于方法本身,如果使用类型约束,您根本不需要进行强制转换,例如:

public static IQueryable<T> _enableFilter<T>(this IQueryable<T> queryable) 
    where T:IEnable 
{
    return queryable.Where(x => (x as IEnable).Enable);
}

我不确定EF Core是否会接受这种非常规的语法。只需添加额外的条件更简单,例如:

_newsRepository.BaseQuery.EnableFilter().FirstOrDefaultAsync(x => x.Id == model.Id && x.Enabled);

全局查询和软删除

如果您想对所有使用特定实体的查询应用过滤条件,例如实现软删除,可以使用全局过滤器。实际上,在文档中,软删除是提到的第一个场景。

在您上下文的OnModelCreating()方法中,您可以添加:

modelBuilder.Entity<SomeEntity>().HasQueryFilter(p => p.IsEnabled);

3
这背后的原因是 EF Core 3 处理此类情况的方式发生了变化。
在 EF Core 2.x 中,这被拉入内存并自动完成操作。由于执行此类操作可能存在潜在的性能问题,EF Core 团队决定将其从默认行为移动到默认行为为错误。
基本上,如果您想要与 2.x 相同的行为,则现在必须在执行操作之前明确将其拉入内存 - 然而,这通常表明最好更改查询的方式 - 除非您确实需要此行为。
有关更多详细信息,请参见此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接