EF Core 8中值对象LINQ查询翻译错误的解决方案咨询
在EF Core 8中,我尝试执行以下LINQ查询(其中Active是封装bool类型的简单值对象):
return await _dbSet .Where(e => e.Active.Value) .ToListAsync();
但收到错误提示:
The LINQ expression 'DbSet()
.Where(e => e.Active.Value)' 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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
相关实体类、ModelBuilder配置及转换器代码如下:
实体类
public abstract class StarSchemaEntity : Entity { public Date Timestamp { get; set; } = new Date(DateTime.Now); public DatabaseBoolean Active { get; set; } = new DatabaseBoolean(true); } public partial class ExecutionEntity : StarSchemaEntity { public Name Name { get; set; } = null!; }
ModelBuilder配置
modelBuilder.Entity<ExecutionEntity>(entity => { entity.HasKey(e => e.Id); entity.ToTable("EXECUTIONS"); entity.Property(e => e.Id) .HasConversion<DatabaseTableIdConverter>() .HasColumnType("NUMBER") .HasColumnName("Id"); entity.Property(e => e.Active) .HasConversion<DatabaseBooleanConverter>() .HasMaxLength(1) .IsUnicode(false) .HasDefaultValueSql("'Y'") .IsFixedLength() .HasColumnName("ACTIVE"); entity.Property(e => e.Name) .HasConversion<NameConverter>() .HasMaxLength(50) .IsUnicode(false) .HasColumnName("NAME"); entity.Property(e => e.Timestamp) .HasConversion<DateConverter>() .HasPrecision(6) .HasDefaultValueSql("CURRENT_TIMESTAMP ") .HasColumnName("TIMESTAMP"); });
转换器
public class DatabaseBooleanConverter : ValueConverter<DatabaseBoolean, string> { public DatabaseBooleanConverter() : base ( v => v.Value.ToString(), v => new DatabaseBoolean(v, true)) { } }
解决方法
EF Core无法直接翻译对值对象内部属性的访问,可通过以下方式处理:
1. 给值对象添加隐式转换
修改DatabaseBoolean类,添加到bool的隐式转换,让值对象可以直接参与布尔判断:
public class DatabaseBoolean { public bool Value { get; } public DatabaseBoolean(bool value) { Value = value; } // 新增隐式转换逻辑 public static implicit operator bool(DatabaseBoolean dbBool) { return dbBool.Value; } }
修改后查询可简化为:
return await _dbSet .Where(e => e.Active) .ToListAsync();
EF Core会通过已配置的转换器,将这个布尔判断转换为数据库层面的字段查询(如ACTIVE = 'Y')。
2. 定义可翻译的表达式方法
在DatabaseBoolean类中添加静态方法,并用[ExpressionBody]标记(EF Core 8支持该特性),让EF能识别并翻译这个方法:
public class DatabaseBoolean { public bool Value { get; } public DatabaseBoolean(bool value) { Value = value; } [ExpressionBody] public static bool IsTrue(DatabaseBoolean dbBool) { return dbBool.Value; } }
查询时调用该方法即可:
return await _dbSet .Where(e => DatabaseBoolean.IsTrue(e.Active)) .ToListAsync();
3. 配置全局查询过滤(适合通用场景)
如果需要对Active字段做全局有效的过滤,可在ModelBuilder中添加查询转换规则:
modelBuilder.Entity<ExecutionEntity>() .Property(e => e.Active) .HasConversion<DatabaseBooleanConverter>() // 定义查询时的转换逻辑 .HasQueryFilter(e => EF.Property<string>(e, "ACTIVE") == "Y");
这种方式会自动给该实体的所有查询加上过滤条件,若只是特定查询需要,建议用前两种方法。
4. 显式启用客户端评估(不推荐)
若以上方式都不适用,可将查询切换为客户端评估——但这会先把全表数据拉到内存再过滤,数据量大时性能极差:
return await _dbSet .AsEnumerable() .Where(e => e.Active.Value) .ToListAsync();
内容的提问来源于stack exchange,提问作者marcosagni98

