对同一实体使用多对多和一对多
问题描述:
我在EF Code-First中有一个多对多关联(如this问题所述),我想使用one-to对同一个实体也是如此。问题是EF不会产生正确的数据库方案。代码:对同一实体使用多对多和一对多
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<B> ObjectsOfB { get; set; }
}
public class B
{
public int Id { get; set; }
public virtual A ObjectA { get; set; }
public virtual ICollection<A> OtherObjectsOfA { get; set; }
}
当我删除B类的ObjectA属性时,正确地生成了多对多关联。 当生成不正确时,实体B将2个外键获取给A,并且实体A获得1个B外键(如多对一关系)。
答
如果您有多个导航属性引用同一个实体,EF不知道其他实体的逆导航属性属于哪里。在你的例子中:A.ObjectsOfB
是指B.ObjectA
还是B.OtherObjectsOfA
?两者都是可能的,而且是有效的模型。
现在,EF不会抛出像“无法明确确定关系”之类的异常。相反,它决定B.ObjectA
引用B
中的第三个端点,该模型中未显示导航属性。这将在表B
中创建第一个外键。 B
中的两个导航属性指的是A
中的两个端点,它们在模型中也未公开:B.ObjectA
创建表B
中的第二个外键,B.OtherObjectsOfA
在表A
中创建外键。
要解决此问题,您必须明确指定关系。
选项之一(最简单的方法)是使用InverseProperty
属性:
public class A
{
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("OtherObjectsOfA")]
public virtual ICollection<B> ObjectsOfB { get; set; }
}
这定义了A.ObjectsOfB
是许多一对多关系B.OtherObjectsOfA
的一部分。
另一种选择是,以流利的API完全定义的关系:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>()
.HasMany(a => a.ObjectsOfB)
.WithMany(b => b.OtherObjectsOfA)
.Map(x =>
{
x.MapLeftKey("AId");
x.MapRightKey("BId");
x.ToTable("ABs");
});
modelBuilder.Entity<B>()
.HasRequired(b => b.ObjectA) // or HasOptional
.WithMany()
.WillCascadeOnDelete(false); // not sure if necessary, you can try it
// without if you want cascading delete
}
谢谢你的回答!但是,对于这两种解决方案,我都会得到以下异常:'在EntityFramework.DLL中发生'System.InvalidOperationException'类型的第一次机会异常' – Marthijn
更正:流畅的解决方案(我的错误..),非常感谢!任何想法为什么'InverseProperty'引发异常? – Marthijn
@Henkie:应用程序真的崩溃吗? “第一次机会异常”通常不是问题,只在调试器输出窗口中显示。通常它可以被忽略。 – Slauma