EF.Property 抛出 "The LINQ expression could not be translated"

EF.Property throws "The LINQ expression could not be translated"

我正在尝试将软删除改造为使用 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,所以我也不想更改用于 return 我的 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);

我可以改变那个变量。

这里的那个东西:

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

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

您不能为 属性 名称使用变量。在这种情况下,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));

问题是用于指定 属性 名称的 _isDeleted 来自派生的 context[=] 的 instance 字段24=],上下文的实例成员在 global query filters 中被特殊对待。基本上它们被参数替换,在这种特殊情况下会导致“不可翻译”的构造。

解决方案是使用staticconst代替,例如

private static readonly string _isDeleted = "IsDeleted";

private const string _isDeleted = "IsDeleted";