在 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_Id
和 Student_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; }
}
但我认为这不是您要找的。所以,我认为你在这里有三个选择:
- 您保留当前的关系配置。
- 创造一个真实的one to one relationship。
- 创建一对多关系
根据我的经验,最后一个最适合您要实现的目标(但这是我的意见)。在这种情况下,您可以根据需要使用 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
我在 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_Id
和 Student_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; }
}
但我认为这不是您要找的。所以,我认为你在这里有三个选择:
- 您保留当前的关系配置。
- 创造一个真实的one to one relationship。
- 创建一对多关系
根据我的经验,最后一个最适合您要实现的目标(但这是我的意见)。在这种情况下,您可以根据需要使用 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