0

我对如何在实体框架中建模我的关系感到困惑。

我想ClassA有选择地指向一个ClassB. 这听起来有点像可选的一对一关系。

我还想ClassB始终指向指向它的 ClassA。我还希望允许多个ClassB's 指代同一个ClassA。这听起来像是一对多的关系。

最好将其建模为一种关系,还是真的是两种完全独立的关系?

如果是两个关系,它们是否可以共享出现在 ClassB 上用于引用的单个属性“ClassAKey” ClassA

在我的问题中,我很清楚我希望我的 OM 是什么:

ClassA
{
    [Key]
    int Key { get; set; }
    ClassB Maybe { get; set; }
    int MaybeKey { get; set; }
}

ClassB
{
    [Key]
    int Key { get; set; }
    ClassA Always { get; set; }
    int AlwaysKey { get; set; }
}

我也很清楚数据库中应该是什么样子:应该只有一个ClassAKey列 forClassB和一个ClassBKeyfor 列,ClassA以便每个列可以相互引用,以及这些列上的外键关系。

但是......我不清楚在 EF 中对此进行建模的方法是什么。事实上,似乎我一定做错了什么!如果我只是从上面的代码开始,我会得到一个错误。

Unable to determine the principal end of an association between the types 'ClassA' and 'ClassB'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

如果我尝试像这样配置它,

modelBuilder.Entity<ClassA>()
            .HasOptional(a => a.Maybe)
            .WithRequired(b => b.Always);

由于某种原因,我在 ClassB 具有错误外键属性的地方生成了看起来像错误的设计器代码

.ForeignKey("dbo.ClassAs", t => t.Key)

它应该是 t.AlwaysKey,对吧?这是怎么回事!?

4

2 回答 2

1

ClassA如果我理解正确,您正在尝试在和之间创建 One:Many 可选关系ClassB。听起来您总是希望ClassB拥有一个关联的ClassA,这将使ClassA您的委托人成为您ClassB的受抚养人。

首先,您必须更改ClassB(在您的ClassA)的导航属性。由于您需要多关系,因此您需要某种集合来存储您的ClassB对象。

这意味着您将删除您的MaybeKeyin ClassA,因为您所指的不是单个对象,而是您现在有一个集合(例如ICollection<ClassB>)。您也不需要相应ClassA表中的列来引用ClassB(因为它是一对多关系,单列不起作用)。表中应该只需要一个外键列ClassB,指ClassAClassB是与之相关的。

您的代码可能如下所示:

public class ClassA
{
    [Key]
    public int Key { get; set; } // ClassA Key

    public virtual ICollection<ClassB> MyClassBs { get; set; } // Your optional dependants.
}

public class ClassB
{  
    [Key]
    public int Key { get; set; } // ClassB key

    public int AKey { get; set; } // Your foreign key.
    public virtual ClassA myClassA { get; set; } // Your principal.
}

导航属性被标记virtual,以便 Entity Framework 可以覆盖它们,并为您提供延迟加载功能(如果您需要)。

要创建映射,您需要按照这些思路做一些事情(如果我误解了您的目标,可能需要进行一些更改)。

modelBuilder.Entity<ClassA>()
            .HasMany(b => b.MyClassBs)     // Many ClassBs
            .WithRequired(a => a.myClassA) // Each ClassB requires a ClassA
            .HasForeignKey(b => b.AKey);   // Use Akey as a foreign key

有几种不同的方式来表达这一点(例如,您可以从ClassB侧面进行)。

可能值得坚持使用稍微更传统的命名,例如ID主键和ClassAID外键名称,因为实体框架有许多可以使用的默认约定。它还使其他人更容易阅读您的代码(因为您往往会遇到类似的约定)。

编辑:更新以包括“特殊”关系。

在您的ClassA中,您可以包含以下字段(您在原始示例中已经拥有,具有不同的名称):

public int SpecialBID { get; set; }
public virtual ClassB SpecialB { get; set; }

并添加以下映射:

modelBuilder.Entity<ClassA>()
            .HasOptional(x => x.SpecialB)
            .WithMany()
            .HasForeignKey(x => x.SpecialBID);

尽管现在无法保证您的收藏SpecialB中包含您的MyClassBs收藏。

您还应该能够完全从 ClassA 中删除 MyClassBs 集合,方法是在关系的另一端(在 classB 对象中)使用不需要导航属性的关系映射:

    modelBuilder.Entity<ClassB>()
        .HasRequired(x => x.myClassA)
        .WithMany()
        .HasForeignKey(x => x.AKey);
于 2013-07-17T13:08:19.193 回答
0

最后,这是我认为应该起作用的,但我没有让它起作用(它可能是 Entity Framework 5 中的一个错误):

modelBuilder.Entity<ClassB>()
    .HasRequired(a => a.Always)
    .WithMany()
    .HasForeignKey(a => a.AlwaysKey);

modelBuilder.Entity<ClassB>()
    .HasOptional(b => b.Maybe);

这就是实际起作用的[注释掉最后两行]:

modelBuilder.Entity<ClassB>()
    .HasRequired(a => a.Always)
    .WithMany()
    .HasForeignKey(a => a.AlwaysKey);

//modelBuilder.Entity<ClassB>()
//    .HasOptional(b => b.Maybe);

我也将 MaybeKey 更改为可为空的 int。

 int? MaybeKey { get; set; }

第二,可选关系按约定工作,而不是显式配置。

于 2013-07-31T19:06:47.400 回答