EfCore OwnsOne 种子可为空的对象失败

Posted

技术标签:

【中文标题】EfCore OwnsOne 种子可为空的对象失败【英文标题】:EfCore OwnsOne seed nullable objects failing 【发布时间】:2022-01-01 17:04:50 【问题描述】:

我想使用 EfCore 将数据播种给我的拥有实体,这些实体可以是 nullable

实体:

public class RootEntity

    protected RootEntity()  

    public Guid Id  get; set; 

    public OwnedEntityLevel1? OwnedEntityLevel1  get; set;  // can be nullable


public class OwnedEntityLevel1

    public Guid Id  get; set; 

DbContext 的模型配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)

    modelBuilder.Entity<RootEntity>(b =>
    
        b.OwnsOne(x => x.OwnedEntityLevel1, ob =>
        
            ob.HasData(RootEntity.All.Select(x => new
                 x.OwnedEntityLevel1?.Id, RootEntityId = x.Id ));
        );
        b.HasData(RootEntity.All.Select(x => new  x.Id ));
    );

当我尝试使用以下方式创建迁移时:

dotnet ef migrations add Initial --context NullableObjectDbContext -o Migrations/NullableObject

我收到错误:

无法添加实体类型“OwnedEntityLevel1”的种子实体,因为没有为所需的属性“Id”提供值。

消息显然是正确的。但我不明白您是否可以以某种方式使用.HasData 为可空对象播种?

我正在尝试播种的数据:

public static RootEntity PredefinedEntity11  get;  =
    new(
        Guid.Parse("96e1d442-bdd0-4c6f-9d01-624b27abbac3"),
        new OwnedEntityLevel1
        
            Id = Guid.Parse("8f8eea73-0b43-412a-b0aa-a9338db6e067")
        
    );

public static RootEntity PredefinedEntity12  get;  =
    new(
        Guid.Parse("aae51dac-016e-472e-ad51-2f09f8cb9fbb"),
        null! // When i add this the migration fails with The seed entity for entity type 'OwnedEntityLevel1' cannot be added because no value was provided for the required property 'Id'
    );

public static IReadOnlyList<RootEntity> All  get;  =
    new List<RootEntity>(new[]  PredefinedEntity11, PredefinedEntity12 ).AsReadOnly();

在我的正常程序流程中,我可以毫无问题地添加可空对象:

var ctx = new NullableObjectDbContext();
var rootEntity = new RootEntity(Guid.NewGuid(), null);
ctx.Add(rootEntity);
ctx.SaveChanges();

我在这里创建了一个最小的可重现示例:https://github.com/enterprisebug/EfCoreHasDataNestedOwnedTypes/tree/main/EfCoreHasDataNestedOwnedTypes/NullableObject

【问题讨论】:

【参考方案1】: 具有匿名类型的

Model data seeding 通过 名称和 type 匹配属性。

在您的情况下,即使种子类型具有名为 Id 的属性,它的类型也不同于种子实体的 Id 属性的类型(Nullable&lt;Guid&gt; 推断自 ?. 运算符与 Guid ),因此未映射并正在生成令人困惑的错误消息。

new
 
    x.OwnedEntityLevel1?.Id, // Guid? Id
    RootEntityId = x.Id      // Guid RootEntityId 

解决方案是通过首先过滤掉null 对象来生成并填充匿名类型的Guid Id 属性,例如(null forgiving operator 用于抑制 NRT 警告):

ob.HasData(RootEntity.All
    .Where(x => x.OwnedEntityLevel1 != null)
    .Select(x => new
     
        x.OwnedEntityLevel1!.Id, // Guid Id
        RootEntityId = x.Id      // Guid RootEntityId
    ));

【讨论】:

谢谢!这解决了我的问题。也感谢您的解释!过滤掉null 值是超级简单的解决方案。我从来没想过!非常感谢您的意见!

以上是关于EfCore OwnsOne 种子可为空的对象失败的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止 EF Core 在可为空的列上创建筛选索引

可为空的对象必须具有一个值

可为空的对象必须具有一个值 是啥原因啊 求解 谢谢

总和可为空的 Linq 查询

解构可为空的对象

在 Typescript 中将可为空的对象值转换为字符串