为什么我的Entity Framework Core的可选一对一关系不起作用?

huangapple go评论57阅读模式
英文:

Why does my optional one-to-one relationship with Entity Framework Core not work?

问题

以下是翻译好的代码部分:

// Principal(父表)
public class Person
{
    [Key]
    public int Id { get; set; }
    public string LastName { get; set; }
    public virtual Address Address { get; set; }
}

// Dependent(子表)
public class Address
{
    public int? AddressRef { get; set; }
    public string Street { get; set; }
    public virtual Person Person { get; set; }
}
modelBuilder.Entity<Person>(entity =>
{
    entity.ToTable("Person");
    entity.HasKey(k => new { k.Id });
    entity.Property(p => p.Id)
            .HasColumnName("PersonRef");
});

modelBuilder.Entity<Address>(entity =>
{
    entity.ToTable("Address");
    entity.HasKey(x => x.AddressRef);
    entity.Property(e => e.AddressRef).ValueGeneratedNever();

    entity.HasOne(a => a.Person)
            .WithOne(b => b.Address)
            .HasForeignKey<Address>(c => c.AddressRef)
            .IsRequired(false)
            .OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_dbo.Address_dbo.Person_AddressRef");
})

如果您需要进一步的帮助,请告诉我。

英文:

I have an existing sql database with 2 tables that have an optional one-to-one relationship with proper records.

I configured the given relationship with Entity Framework Core for my asp.net core web application as given below:

// Principal (parent)
public class Person
{
    [Key]
    public int Id { get; set; }
    public string LastName { get; set; }
    public virtual Address Address { get; set; }
}

// Dependent (child)
public class Address
{
    public int? AddressRef { get; set; }
    public string Street { get; set; }
    public virtual Person Person { get; set; }
}
modelBuilder.Entity&lt;Person&gt;(entity =&gt;
{
    entity.ToTable(&quot;Person&quot;);
    entity.HasKey(k =&gt; new { k.Id });
    entity.Property(p =&gt; p.Id)
            .HasColumnName(&quot;PersonRef&quot;);
});

modelBuilder.Entity&lt;Address&gt;(entity =&gt;
{
    entity.ToTable(&quot;Address&quot;);
    entity.HasKey(x =&gt; x.AddressRef);
    entity.Property(e =&gt; e.AddressRef).ValueGeneratedNever();

    entity.HasOne(a =&gt; a.Person)
            .WithOne(b =&gt; b.Address)
            .HasForeignKey&lt;Address&gt;(c =&gt; c.AddressRef)
            .IsRequired(false)                  
            .OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName(&quot;FK_dbo.Address_dbo.Person_AddressRef&quot;);
})

When I try to add a new Person via DbContext

var newPerson = new Person()
{
    LastName = model.LastName,
    // ...
};
DbContext.Add(newPerson);
DbContext.SaveChanges()   // Error is thrown

I get the ErrorMessage:

> The value of 'Person.Id' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known.'

I have tried several variations for configuring the one-to-one relationship but all give the same error.

Any help would gratefully be appreciated.

答案1

得分: 1

以下是翻译的代码部分:

"Your principal entity's Primary Key should be annotated as AutoGeneratedValue.

modelBuilder.Entity<Person>(entity =>
{
    entity.ToTable("Person");
    entity.HasKey(k => k.Id); // .ValueGeneratedOnAdd(); problem was here
    entity.Property(k => k.Id).ValueGeneratedOnAdd(); // <-- Here is the change
    entity.Property(k => k.Id)
          .HasColumnName("PersonRef");
});

Note: 主键 应该在 主实体 中被注释为 AutoGeneratedValue,因为它将被用作 从属实体 中的主键。

并且,正如 @FFad3 所尝试告诉的,在您的情况下,Address 是可选的,而不是如我上面提到的 Person。Address 的主键应该从 Person 表中的现有记录映射而来。您不能有一个 Address 记录而没有对应的人。

您可以将其更改为一对多的关系,以使这成为可能。这样,在 Person 表中,您将有一个与相关 Address外键(可空外键),以及 Address 实体上的单独的 主键

如果您问我,我会解释给您如何做到这一点。"

英文:

Your principal entity's Primary Key should be a annotated as AutoGeneratedValue.

 modelBuilder.Entity&lt;Person&gt;(entity =&gt;
 {
     entity.ToTable(&quot;Person&quot;);
     entity.HasKey(k =&gt; k.Id); // .ValueGeneratedOnAdd(); problem was here
     entity.Property(k =&gt; k.Id).ValueGeneratedOnAdd(); // &lt;-- Here is the change
     entity.Property(k =&gt; k.Id)
           .HasColumnName(&quot;PersonRef&quot;);
 });

Note: Primiary key should have a value for Primary Key in Principal Entity because it will be used as primary key in the Dependent Entity

And as @FFad3 tried to tell, in your scenario Address is Optional not Person as I mentioned above the primary key of the Address should be Mapped from an existing record on Person table. You can't have a record of Address without having a corresponding person.

You may change it to one-to-many relation to make it possible. in this way in Person table you will have a Foreign Key for related Address (nullable foreign key) and separated Primary key on Address entity.

If you ask me I will explain you how to do this.

答案2

得分: 0

我认为你应该更改

public virtual Address Address { get; set; }

public virtual Address? Address { get; set; }

同时检查迁移脚本,确保Person.Id已设置为SqlServer:Identity:

Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1")
英文:

I think that you should change

public virtual Address Address { get; set; } => public virtual Address? Address { get; set; }

also check migration script if Person.Id is set to SqlServer:Identity

Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1")

huangapple
  • 本文由 发表于 2023年4月6日 23:44:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75951386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定