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

本文关键字:关系 withoptionaldependent 实体 映射 HasOptional 框架 | 更新日期: 2023-09-27 18:10:37

我在实体框架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);
    }
}

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

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

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

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

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

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

在GitHub上询问后,我设法从实体框架程序管理器获得响应。

不幸的是,这是EF6的限制。在一对一关系中不能有外键属性,除非它也是主键属性。这主要是因为EF6不支持备用键/唯一索引,所以你不能强制非主键属性是唯一的。事实上,当外键属性不在实体中时,你可以这样做,这有点奇怪……但显然我们不会删除 。

BTW备用键(因此这种场景)在EF Core中是支持的。

mdash;罗文·米勒@https://github.com/aspnet/EntityFramework6/issues/159 issuecomment - 274889438

如果您想在依赖实体中以一对一的关系声明FK属性,恐怕您也必须将其用作PK。EF Code First要求依赖实体的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);