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 应用程序。
在DbContext
的OnModelCreating
方法中是这样完成的:
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<bool>(m, "IsDeleted")
,而是使用EF.Property<bool>(m, isDeleted)
where @987654334 @ 是一些捕获的变量(类字段)。请确保提供重现问题的确切代码。
@IvanStoev 我已经用确切的代码编辑了我的问题
【参考方案1】:
问题是用于指定属性名称的_isDeleted
来自派生context 的instance 字段,并且上下文的实例成员在global query filters。基本上它们被替换为参数,在这种特殊情况下会导致“不可翻译”结构。
解决方案是改用static
或const
,例如
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 表达式”的主要内容,如果未能解决你的问题,请参考以下文章