C# 映射实体框架6中HasOptional()与OptionalDependent()关系中的外键

C# 映射实体框架6中HasOptional()与OptionalDependent()关系中的外键,c#,entity-framework,fluent-entity-framework,C#,Entity Framework,Fluent 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

我在实体框架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);
    }
}
但是,现在我想添加
Student\u Id
属性,以便在
Contact
实体中可用。因此,我可以阅读
Student\u Id
,而无需通过
.Student.Id
导航加入其他表

如果我将属性添加到
联系人
实体中,则会出现两列
Student\u Id
Student\u Id 1
,或者会出现一条错误消息,提示类型中的每个属性名称必须是唯一的。


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

如果要在一对一关系中的从属实体中声明FK属性,恐怕也必须将其用作PK。EF首先编码依赖实体的PK也必须是关系的FK:

public class Contact
{
    [Key,ForeignKey("Student")]
    public int StudentId { get; set; }
    public virtual Student Student { get; set; }
}
但我认为这不是你想要的。所以,我认为你有三个选择:

  • 您将保留当前的关系配置
  • 创造一个真实的世界
  • 创建一对多关系
根据我的经验,最后一个是最适合你想要实现的(但这是我的观点)。在这种情况下,您可以根据需要使用Fk属性,唯一的问题是您需要通过集合更改
Student
上的
Contact
导航属性(或省略此导航属性并创建单向关系):

公共班级学生
{
公共int Id{get;set;}
公共虚拟ICollection联系人{get;set;}
}
配置如下所示:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany(x => x.Contacts)
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);
builder.Entity()
.has可选(x=>x.Student)
.具有多个(x=>x个联系人)
.HasForeignKey(x=>x.StudentId)
.WillCascadeOnDelete(真);
更新 第四个选项是创建两个单向关系:

 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);
builder.Entity()
.has可选(x=>x.Student)
.有很多
.HasForeignKey(x=>x.StudentId)
.WillCascadeOnDelete(真);
builder.Entity()
.has可选(x=>x.Contact)
.有很多
.HasForeignKey(x=>x.ContactId)
.WillCascadeOnDelete(真);

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

我在之后设法从实体框架程序经理那里得到了一个响应


不幸的是,这是EF6的一个限制。在一对一关系中不能有外键属性,除非它也是主键属性。这本质上是因为EF6不支持备用键/唯一索引,所以不能强制非主键属性是唯一的。当外键属性不在实体中时可以执行此操作,这有点奇怪。。。但是很明显,我们不会删除某些内容。您可以通过将FK字段添加到模型中来阻止EF隐式创建FK字段:public int Student_Id{get;set;}然后用注释或fluent指示这是导航属性的外键。@SteveGreene:不过,正如我在问题中所述,EF不让我这么做。它要么创建两列,要么抱怨属性名称的唯一性。我尝试将字段添加到模型中,甚至尝试使用MapKey函数配置关系,但没有成功。对,您需要告诉EF如何与HasForeignKey或MapKey建立关联@史蒂文·格林:谢谢你的帮助。HasForeignKey的问题在于,它仅在具有许多关系的
中可用。当我尝试使用MapKey时,我要么得到两列,要么得到我提到的错误。这个解决方案的问题是,它迫使我建立一对多关系。我不能在那里集合。我不想改变关于我的模型的任何东西,我不想改变算术,它会给我带来太多的问题,太多的开销。我只想读取数据库表中的列。如果要创建一对一关系,并且每个实体都有自己的PK,恐怕唯一的选择是您已经有了。抱歉,无法使用该配置映射FK列谢谢您的帮助。这有什么原因吗?我的意思是,专栏已经在那里了,我只是想读一下。如果实体框架的这种限制是不合理的,那就太奇怪了。我真的不知道EF在内部是如何管理的,事实上,你想要实现的不是一对一的关系,而是一对一的关系。两个实体必须共享同一PK。也许你可以在这个关系中找到关于这个主题的更多信息。这个关系是0..1到0..1,这意味着甚至不可能共享PK。EF6.1支持外键引用非主唯一键:-自2014年以来就是这样。所以我很好奇为什么链接的github帖子是2017年发布的。我现在很困惑。
 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);