如何获得组合的可选关系和一对多关系?

时间:2013-07-16 23:45:38

标签: entity-framework foreign-keys relationships data-modeling

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

我希望ClassA可选择指向ClassB。 这听起来有点像可选的一对一关系。

我还希望ClassB始终指向指向它的ClassA。 我还想允许多个ClassB引用相同的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只有一个ClassB列,而ClassBKey应该只有一个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,对吗? 发生了什么事?!

2 个答案:

答案 0 :(得分:1)

如果我理解正确,您正在尝试在ClassAClassB之间创建一个:多个可选关系。听起来您似乎总是希望ClassB拥有关联的ClassA,这会使ClassA成为您的主体,而ClassB则是您的主体。

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

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

您的代码可能如下所示:

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,因此实体框架可以覆盖它们,并为您提供延迟加载功能(如果需要)。

要创建您的映射,您可以按照以下方式执行某些操作(如果我误解了您的目标,可能需要进行一些更改)。

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等名称,因为实体框架的数量为default conventions可以利用。它还使其他人更容易阅读您的代码(因为您倾向于遇到类似的约定)。

修改:已更新为包含“特殊”关系。

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);

答案 1 :(得分: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? MaybeKey { get; set; }

第二个可选关系按惯例工作,而不是显式配置。