在HasOptional()中映射外键.实体框架6中的WithOptionalDependent()关系

Tom*_*rek 14 c# entity-framework fluent-entity-framework

我在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我启动这段代码时,我得到了正确的表格结构:

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

dbo.Students
    Id (PK)
Run Code Online (Sandbox Code Playgroud)

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

如果我的属性添加到Contact实体,我最终要么有两列Student_IdStudent_Id1,或者我结束了一个错误信息说Each property name in a type must be unique..

该列已经在数据库中,我需要的只是将它放在实体中,为什么这么麻烦?有解决方案吗?

Tom*_*rek 12

向GitHub询问后,我设法得到了实体框架项目经理的回复.

不幸的是,这是EF6的限制.您不能以一对一的关系拥有外键属性,除非它也是主键属性.这主要是因为EF6不支持备用键/唯一索引,因此您无法强制非主键属性是唯一的.当外键属性不在实体中时可以这样做的事实有点怪癖......但显然不是我们要删除的东西.

EF Core支持BTW备用密钥(因此也支持此方案).

- Rowan Miller @ https://github.com/aspnet/EntityFramework6/issues/159#issuecomment-274889438


oct*_*ccl 8

如果你想以一对一的关系在依赖实体中声明FK属性,我恐怕你也必须将它用作PK.EF Code First 要求依赖实体的PK也必须是关系的FK:

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

但我认为这不是你想要的.所以,我认为你有三个选择:

  • 您保留当前的关系配置.
  • 创建真实的一对一关系.
  • 创建一对多的关系

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

public class Student
{
    public int Id { get; set; }
    public virtual ICollection<Contact> Contacts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

配置将是这样的:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany(x => x.Contacts)
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);
Run Code Online (Sandbox Code Playgroud)

更新

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

 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);
Run Code Online (Sandbox Code Playgroud)

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