代码优先实体框架6:1对1与复合键



我继承了一个遗留的SQL数据库,我正试图将其逆向工程为代码优先。由于遗留软件在当前配置上运行,因此不能更改模式。对我来说奇怪的是,开发人员将子表配置为具有由ChildId + ParentId组成的复合主键,而不仅仅是ParentId。

我有:

public class Parent
{
    public decimal PkParentId { get; set; }
    public string ParentName { get; set; }
    ...
    public Child Child { get; set; }
}

public class Child
{
    public decimal PkChildId { get; set; }
    public decimal PkParentId { get; set; }
    public string ChildName { get; set; }
    ...
    public Parent Parent { get; set; }
}

这是我的映射。当我对它进行反向工程时,EF想要在父节点中创建一个具有子节点集合的1对多,但我真正想要的是1对0或1(单个子节点可能存在,也可能不存在,但父节点必须存在)。

public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
    public ParentConfiguration()
    {
        HasKey(p => p.PkParentId);
        Property(p => p.PkParentId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasOptional(p => p.Child).WithRequired(p => p.Parent);
        // Also tried this
        // HasOptional(p => p.Child).WithRequired(p => p.Parent).Map(p => p.MapKey("PkParentId"));
    }
}
public class ChildConfiguration : EntityTypeConfiguration<Child>
{
    public ChildConfiguration()
    {
        // Pk is composite on child
        HasKey(e => new {e.PkChildId, e.PkParentId});
        Property(p => p.PkChildId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

我可以同时获取父/子元素没有问题,但如果我试图插入父/子元素:

        var newParent = new Parent
        {
            ParentName = "XXX",
            Child = new Child { ChildName = "YYY" }
        };
        _dbContext.Parents.Add(newParent);
        _dbContext.SaveChanges();

这会导致以下错误:在SET子句中多次指定列名'PkParentId'。在同一个SET子句中,一个列不能被赋多个值。修改SET子句以确保列只更新一次。如果SET子句更新了视图的列,那么列名'PkParentId'可能会在视图定义中出现两次。

可以肯定的是,如果检查生成的SQL,它试图插入PkParentId两次与子插入,一次作为第一个参数,一次作为最后一个参数。这就像EF没有把PkParentId放在一起是父导航属性的外键所以我试着把它添加到子配置中:

HasRequired(x => x.Parent).WithOptional(y => y.Child).Map(z => z.MapKey("PkParentId"));

会产生相同的错误。奇怪的是,我找不到一个可选的关系,让我使用"HasForeignKey"除了WithMany()强制我回到1:m

EF中的一对一总是包含一个主键,同时也是一个外键。子键的主键复制父键的值,并通过FK约束引用它们。

但是子节点的主键与父节点的完全匹配!它没有。

但这是只有你知道的事。EF不需要知道Child有一个组合键。把PkChildId标记为computed:

public ChildConfiguration()
{
    // Pk is composite on child
    HasKey(e => e.PkParentId);
    Property(p => p.PkChildId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
}

现在EF只能通过PkParentId来识别Parent s和Child ren,当插入Child时,它只会从数据库中读取PkChildId,而不会将其用作实体键

相关内容

  • 没有找到相关文章

最新更新