EF.Property 抛出“无法翻译 LINQ 表达式”

Posted

技术标签:

【中文标题】EF.Property 抛出“无法翻译 LINQ 表达式”【英文标题】:EF.Property throws "The LINQ expression could not be translated" 【发布时间】:2022-01-11 21:24:36 【问题描述】:

我正在尝试将软删除改造为使用 EF Core 的 ASP.NET Core 5.0 应用程序。

DbContextOnModelCreating方法中是这样完成的:

builder.Entity<MyEntity>().Property<bool>("IsDeleted");
builder.Entity<MyEntity>().HasQueryFilter(m => EF.Property<bool>(m, "IsDeleted") == false);

这是在docs 中推荐的。我希望避免更改底层数据库实体(如MyEntity)。

异常是在这样的代码上引发的,它曾经完美地工作:

var myEntities= _context.MyEntities.AsNoTracking();
return _mapper.Map<IEnumerable<MyEntity>>(myEntities);

导致(在第二行):

InvalidOperationException: The LINQ expression 'DbSet<MyEntity>()
    .Where(s => EF.Property<bool>(s, __ef_filter___isDeleted_0) == False)' could not be translated.

因为(自然)有很多地方使用 AutoMapper,所以我也不想更改用于返回我的 DTO 的代码。调整 AutoMapper 配置就可以了。

PostgreSQL 被用作底层数据库,如果这有什么不同的话。

我需要如何配置我的查询过滤器,以便现有代码不会引发异常?

// 编辑:

我已经简化了我的示例,确切的代码使用字符串作为属性名称:

private readonly string _isDeleted = "IsDeleted";

builder.Entity<MyEntity>().Property<bool>(_isDeleted);
builder.Entity<MyEntity>().HasQueryFilter(m => EF.Property<bool>(m, _isDeleted) == false);

我可以改变那个变量。

【问题讨论】:

它是否有效_context.MyEntities.AsNoTracking().ToList() @SvyatoslavDanyliv 不,如果我添加这个,则会抛出相同的错误,就在第一行(我添加了.ToList() 所以不是 Automapper 的问题。是否启用了任何扩展?喜欢 LINQKit? 看起来您的示例具有误导性,因为异常消息的__ef_filter___isDeleted_0 部分表明您使用EF.Property&lt;bool&gt;(m, "IsDeleted"),而是使用EF.Property&lt;bool&gt;(m, isDeleted) where @987654334 @ 是一些捕获的变量(类字段)。请确保提供重现问题的确切代码。 @IvanStoev 我已经用确切的代码编辑了我的问题 【参考方案1】:

问题是用于指定属性名称的_isDeleted 来自派生contextinstance 字段,并且上下文的实例成员在global query filters。基本上它们被替换为参数,在这种特殊情况下会导致“不可翻译”结构。

解决方案是改用staticconst,例如

private static readonly string _isDeleted = "IsDeleted";

private const string _isDeleted = "IsDeleted";

【讨论】:

【参考方案2】:

这里的东西:

HasQueryFilter(b => EF.Property<string>(b, "_tenantId") == _tenantId);

已完成,因为该字段是私有的。 您仍然需要实体上的字段才能完成这项工作。但是,您可以在您的 automapper 配置中忽略它,这样您就不需要调整您的 dto。

【讨论】:

【参考方案3】:

您不能使用变量作为属性名称。在这种情况下,EF 创建在这种情况下不可翻译的参数。

解决方案是通过辅助函数动态生成查询过滤器:

public static Expression<Func<T, bool>> GenerateDeletedFilter<T>(string propName)

    var param = Expression.Parameter(typeof(T), "e");

    var filter = Expression.Lambda<Func<T, bool>>(
        Expression.Equal(
            Expression.Call(typeof(EF), nameof(EF.Property), new[]  typeof(bool) , param,
                Expression.Constant(propName)), Expression.Constant(false)), param);

    return filter;

及用法:

private readonly string _isDeleted = "IsDeleted";

builder.Entity<MyEntity>().Property<bool>(_isDeleted);
builder.Entity<MyEntity>().HasQueryFilter(GenerateDeletedFilter<MyEntity>(_isDeleted));

【讨论】:

以上是关于EF.Property 抛出“无法翻译 LINQ 表达式”的主要内容,如果未能解决你的问题,请参考以下文章

无法翻译 LINQ 表达式,将在本地计算

无法翻译 LINQ 表达式 DbSet<>.Any

无法翻译 LINQ 表达式。以可翻译的形式重写查询,或切换到客户端评估 EF Core 3.1

Linq 得到条件计数

LINQ - 按属性名称选择

为啥我们不能只抛出异常/可抛出而不是有多个异常[重复]