使用多对多和一对多关系连接同一实体

8

我在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的外键(就像一个多对一的关系)。

2个回答

18
如果您有多个导航属性引用相同的实体,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 之间的多对多关系的一部分。

另一个选项是完全在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
}

谢谢您的回答!但是,对于这两个解决方案,我都遇到了以下异常:EntityFramework.DLL中发生了类型为“System.InvalidOperationException”的第一次机会异常。 - Marthijn
更正:流畅的解决方案可行(我的错误..),所以谢谢!有任何想法为什么“InverseProperty”会抛出异常吗? - Marthijn
@Henkie:应用程序真的崩溃了吗?“First chance exception”通常不是问题,只会在调试器输出窗口中显示。通常可以忽略它。 - Slauma
应用程序确实没有崩溃,但数据库方案未生成。我正在使用VS Express,因此无法在第一次机会异常时中断。 - Marthijn
[InverseProperty] 标签对我非常有用,非常感谢! - L4marr

0

如果表B有指向表A的外键,则类B具有导航属性到A,而A具有导航属性到ICollection<A>
如果表B与表A存在多对多关系,则类A必须具有ICollection<B>,类B必须具有ICollection<A>

试试这样,也许这会澄清您对EF的请求。


我在代码示例中忘记给“类A”添加ICollection以进行多对多关联。现已修复。 - Marthijn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接