EF Core 8 CosmosDB查询含值对象列表实体时遇Nullable异常
解决EF Core 8 Cosmos查询值对象列表时的Nullable异常问题
问题根源分析
你遇到的System.InvalidOperationException: Nullable object must have a value异常,核心原因有两个:
Marker类继承了带标识属性(如Id)的BaseEntity,但你将其作为值对象集合通过ComplexProperty映射,EF Core Cosmos会尝试绑定标识属性,但数据库中该字段不存在或为null,导致非空断言失败。- 实体类中
Marker的ShortName和Name被标记为非空(null!),但如果数据库中对应的Markers元素存在null值,EF Core映射时会强制赋值null给非空属性,触发异常。
解决方案
1. 将Marker修正为纯值对象(推荐)
值对象不需要身份标识(Id),因此移除对BaseEntity的继承,并确保属性可空性与数据匹配:
public sealed class Marker // 移除BaseEntity继承 { public Marker(string name, decimal? lowerNorm = null, decimal? upperNorm = null, string? unit = null, string? shortName = null) { ShortName = shortName ?? name; Name = name; LowerNorm = lowerNorm; UpperNorm = upperNorm; Unit = unit; } public string ShortName { get; private set; } = null!; public string Name { get; private set; } = null!; public decimal? LowerNorm { get; private set; } public decimal? UpperNorm { get; private set; } public string? Unit { get; private set; } // 值对象相等性实现(可选规范) public override bool Equals(object? obj) { if (obj is not Marker other) return false; return ShortName == other.ShortName && Name == other.Name && LowerNorm == other.LowerNorm && UpperNorm == other.UpperNorm && Unit == other.Unit; } public override int GetHashCode() { return HashCode.Combine(ShortName, Name, LowerNorm, UpperNorm, Unit); } }
2. 调整EF Core映射配置
针对值对象集合,确保ComplexProperty配置正确,同时禁用不必要的标识映射:
modelBuilder.Entity<Test>(entity => { entity.ToContainer("Tests") .HasNoDiscriminator() .HasPartitionKey(r => r.ShortName); entity.Property(r => r.Id).ToJsonProperty("id"); // 配置Markers为复杂类型集合,显式指定属性映射 entity.ComplexProperty(r => r.Markers, cp => { cp.IsRequired(); cp.Property(m => m.ShortName).ToJsonProperty("shortName").IsRequired(); cp.Property(m => m.Name).ToJsonProperty("name").IsRequired(); cp.Property(m => m.LowerNorm).ToJsonProperty("lowerNorm"); cp.Property(m => m.UpperNorm).ToJsonProperty("upperNorm"); cp.Property(m => m.Unit).ToJsonProperty("unit"); }); });
3. 修复数据库中现有数据的不一致性
检查Cosmos容器中Tests文档的Markers数组,确保:
- 每个Marker元素的
shortName和name字段不为null(对应实体类的非空属性) - 不存在null的Marker元素
4. 临时调试规避方案(不推荐)
如果暂时无法修改实体或数据,可将Marker的非空属性改为可空,保留null!断言作为临时措施:
public string? ShortName { get; set; } = null!; public string? Name { get; set; } = null!;
验证步骤
- 重新生成迁移并更新Cosmos容器(若使用迁移)
- 执行查询操作,确认异常消失,Markers列表能正确加载
内容的提问来源于stack exchange,提问作者kamildobkowski
相关产品推荐
相关产品推荐

