在 Entity Framework 6 中映射 HasOptional().WithOptionalDependent() 关系中的外键

Mapping foreign key in HasOptional().WithOptionalDependent() relation in Entity Framework 6

我在 Entity Framework 6.1.3 中有以下数据模型:

using System.Data.Entity;

public class Student
{
    public int Id { get; set; }
    public virtual Contact Contact { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public virtual Student Student { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder builder)
    {
        builder.Entity<Contact>()
            .HasOptional(x => x.Student)
            .WithOptionalDependent(x => x.Contact)
            .WillCascadeOnDelete(true);
    }
}

public static class Program
{
    private static void Main()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

        using (var context = new MyContext())
            context.Database.Initialize(force: true);
    }
}

当我启动这段代码时,我得到了我想要的完全正确的 table 结构:

dbo.Contacts
    Id (PK)
    Student_Id (FK, NULL, CASCADE ON DELETE)

dbo.Students
    Id (PK)

但是,现在我想添加 Student_Id 属性 以便在 Contact 实体中可用。所以我可以阅读 Student_Id 而无需通过 .Student.Id 导航加入其他 table。

如果我将 属性 添加到 Contact 实体,我最终要么得到两列 Student_IdStudent_Id1,要么得到一条错误消息说 Each property name in a type must be unique..

该列已经在数据库中,我只需要在实体中也有它,为什么这么麻烦?有解决办法吗?

如果你想在一对一的依赖实体中声明FK属性,恐怕你也必须将其用作PK。 EF Code First requires 依赖实体的 PK 也必须是关系的 FK:

public class Contact
{
    [Key,ForeignKey("Student")]
    public int StudentId { get; set; }
    public virtual Student Student { get; set; }
}

但我认为这不是您要找的。所以,我认为你在这里有三个选择:

根据我的经验,最后一个最适合您要实现的目标(但这是我的意见)。在这种情况下,您可以根据需要使用 Fk 属性,唯一的是您需要通过集合更改 Student 上的 Contact 导航 属性(或省略此导航 属性 并创建单向关系):

public class Student
{
    public int Id { get; set; }
    public virtual ICollection<Contact> Contacts { get; set; }
}

配置是这样的:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany(x => x.Contacts)
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

更新

第四个选项可以创建两个单向关系:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany()
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

 builder.Entity<Student>()
        .HasOptional(x => x.Contact)
        .WithMany()
        .HasForeignKey(x => x.ContactId)
        .WillCascadeOnDelete(true);

但是这个选项破坏了两个表之间的真实关系。

asking on GitHub 之后,我设法得到了 Entity Framework 项目经理的回复。

Unfortunately this is a limitation of EF6. You can not have a foreign key property in a one-to-one relationship, unless it is also the primary key property. This is essentially because EF6 doesn't support alternate keys/unique indexes, so you can't enforce that a non-primary key property is unique. The fact that you can do it when the foreign key property isn't in the entity is a bit of a quirk... but obviously not something we would remove .

BTW alternate keys (and therefore this scenario) is supported in EF Core.

–罗文米勒@ https://github.com/aspnet/EntityFramework6/issues/159#issuecomment-274889438