【问题标题】:Mapping foreign key in HasOptional().WithOptionalDependent() relation in Entity Framework 6在实体框架 6 中映射 HasOptional().WithOptionalDependent() 关系中的外键
【发布时间】:2015-11-25 15:23:22
【问题描述】:

我在 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);
    }
}

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

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.

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

【问题讨论】:

  • 您可以通过将 FK 字段添加到您的模型来防止 EF 隐式创建 FK 字段: public int Student_Id { get;放;然后用注释或流利的方式指出这是导航属性的外键。
  • @SteveGreene:我试图这样做,但是,正如我在问题中所说,EF 不允许我这样做。它要么创建两列,要么抱怨属性名称的唯一性。我尝试将字段添加到模型中,我什至尝试使用 MapKey 函数配置关系,但没有成功。
  • 对,你需要告诉EF如何与HasForeignKey或MapKey进行关联。 patrickdesjardins.com/blog/…
  • @SteveGreene:感谢您的帮助。 HasForeignKey 的问题在于它仅在WithMany 关系中可用。当我尝试使用 MapKey 时,我会得到两列或者我提到的错误。

标签: c# entity-framework fluent-entity-framework


【解决方案1】:

如果你想在依赖实体中以一对一的关系声明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);

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

【讨论】:

  • 这个解决方案的问题是它迫使我建立一对多的关系。我不能在那里拥有 ICollection。我不想改变我的模型的任何东西,我不想改变arity,它会给我增加很多问题,很多开销。我只想读取数据库表中的列。
  • 如果你想建立一对一的关系并且每个实体都有自己的PK,恐怕唯一的选择就是你已经拥有了。抱歉,无法使用该配置映射 FK 列
  • 感谢您的帮助。这有什么原因吗?我的意思是,该专栏已经存在,我只想阅读它。如果 Entity Framework 的这种限制是不合理的,那就太奇怪了。
  • 我真的不知道 EF 是如何在内部管理的,事实上,你想要实现的并不是真正的一对一关系,而是一对一的关系。两个实体必须共享相同的 PK。也许您可以在link 中找到有关此主题的更多信息
  • 这个关系是0..1到0..1,也就是说连PK都不可能共享了。
【解决方案2】:

asking on GitHub 之后,我设法从实体框架项目经理那里得到了回复。

很遗憾,这是 EF6 的限制。您不能在一对一关系中拥有外键属性,除非它也是主键属性。这本质上是因为 EF6 不支持备用键/唯一索引,因此您不能强制非主键属性是唯一的。当外键属性不在实体中时您可以这样做的事实有点怪癖......但显然我们不会删除?。

EF Core 支持 BTW 备用键(因此也支持此方案)。

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

【讨论】:

  • EF6.1 确实支持外键引用非主唯一键:stackoverflow.com/questions/18889218/… - 自 2014 年以来就是这种情况。所以我很好奇为什么链接的 github 帖子是 2017 年的。我现在很困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多