在EF查询Where子句中能否直接使用ValueObject作为筛选条件?
问题原因
你遇到的报错是EF Core对Owned类型(你用OwnsOne配置的ValueObject)的表达式解析逻辑导致的:
EF Core在解析u.Username.Equals(username)查询时,会将Owned类型当成实体处理,默认尝试比对两个实体的主键,由于你没有给Username配置主键,EF会自动查找隐式主键字段,才会抛出找不到Username.UserDbId的错误。此外EF需要将查询表达式转为SQL执行,不会直接调用你在C#中定义的Equals内存方法,因此你重载的ValueObject比较逻辑不会生效。
解决方案
你提到的值转换器(Value Converter)是实现直接用ValueObject做查询比较的最优方案,配置方式如下:
- 首先定义
Username对应的值转换器:
public class UsernameConverter : ValueConverter<Username, string> { public UsernameConverter() : base( convertToProviderExpression: v => v.Value, convertFromProviderExpression: v => new Username(v)) { } }
- 调整你的
User实体映射配置,替换原来的OwnsOne配置段:
internal class UserMap : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("Users"); builder.Property<int>("DbId").HasColumnName("DbId").UseIdentityColumn(); builder.HasKey("DbId"); // 替换原有OwnsOne配置 builder.Property(u => u.Username) .HasConversion<UsernameConverter>() .HasColumnName("Username") .IsRequired(); builder.HasIndex(u => u.Username).IsUnique(); } }
配置完成后,你最初的u => u.Username.Equals(username)写法就可以正常运行,EF会自动将比较逻辑转换为SQL层面的字段等值判断,不需要再手动访问Value属性。
补充说明
如果你的ValueObject对应多个数据库字段,不适合用值转换器处理,也可以通过自定义EF Core表达式翻译器,将ValueObject的Equals调用自动翻译为多字段的等值比对逻辑,不过实现复杂度更高,对于单字段值对象来说值转换器的方案足够简洁高效。
内容的提问来源于stack exchange,提问作者Neil W
相关产品推荐
相关产品推荐

