对同一实体同时使用多对多和一对多

本文关键字:一对多 实体 | 更新日期: 2023-09-27 18:15:29

我在EF Code-First中有一个多对多的关联(正如在这个问题中解释的那样),我也想对同一个实体使用一对多的关联。问题是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.ObjectsOfBB.OtherObjectsOfA的多对多关系的一部分。

另一个选择是在Fluent 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
}

如果表B有指向表A的外键,则类B有指向表A的导航属性,类A也有指向ICollection<A>的导航属性。
如果表B和表A有多对多关系,则类A必须有ICollection<B>,类B必须有ICollection<A>

试试,也许这会让EF明白你的要求。