使用外键引用映射多对多关系

对于精通EF的用户来说,这应该是一个简单的问题。

我有以下模式(在我的脑海中)表示表之间的关系应该如何。

[FooBar] [Foo] [Bar] FooId PK,FK Id PK Id PK BarId PK,FK BarId FK Name IsRead Name Description Description 

但是,当我尝试使用EF代码生成模式时,首先它无法解释实体之间的关系,因为我已经解释了它们(将外键FooId添加到[bar]表中)并且无法完全创建[FooBar]桥牌桌。

如果有人可以指导我如何使用EF4代码实现上述架构 – 首先我很感激。 只要创建了所需的数据库模式,解决方案是否涉及我的POCO模型上的属性,流畅的配置或两者的混合都无关紧要。


POCO型号:

 public class Foo { public int Id { get; set; } public string Text { get; set; } public string Description { get; set; } public int BarId { get; set; } public Bar Bar { get; set; } /* bar entity */ public virtual ICollection BridgedBars { get; set; } public Foo() { Bars = new List(); } } public class Bar { public int Id { get; set; } public string Text { get; set; } public string Description { get; set; } public virtual ICollection Foos { get; set; } public virtual ICollection BridgedFoos { get; set; } public Bar() { Foos = new List(); BridgedFoos = new List(); } } public class FooBar { public int FooId { get; set; } public int BarId { get; set; } public virtual Foo Foo { get; set; } public virtual Bar Bar { get; set; } public bool IsRead { get; set; } } 

您的模型确实会在Bar创建一个外键FooId ,它属于Foo.BrideBars定义的关系。 EF不会将此导航属性与Bar中的ICollection属性之一相关联,因为它们中有两个并且EF无法唯一地确定哪个是正确的对。 因此,它为Foo.BrideBars创建了一个关系,而另一端没有导航属性。 可以这么说,有一个不可见的Bar.Foo属性导致外键。

要映射到模型的数据库模式实际上并不代表多对多关系,而是与中间“桥”实体FooBar两个一对多关系。 您必须在导航属性中使用此类来定义正确的关系。 它看起来像这样:

 public class Foo { public int Id { get; set; } public string Text { get; set; } public string Description { get; set; } public int BarId { get; set; } public Bar Bar { get; set; } public virtual ICollection FooBars { get; set; } } public class Bar { public int Id { get; set; } public string Text { get; set; } public string Description { get; set; } public virtual ICollection Foos { get; set; } public virtual ICollection FooBars { get; set; } } public class FooBar { [Key, Column(Order = 0)] public int FooId { get; set; } [Key, Column(Order = 1)] public int BarId { get; set; } public virtual Foo Foo { get; set; } public virtual Bar Bar { get; set; } public bool IsRead { get; set; } } 

将通过此模型中的命名约定检测正确的关系。 仅对于FooBar实体,必须显式定义键,因为属性名称不符合约定(没有Id和没有FooBarId属性)。 在这个模型中,在FooBar使用复合键是FooBar

我想,你的真正的类和属性没有名字FooBar 。 如果您的真实姓名不遵循惯例,则可能需要指定与注释的关系 – 或使用Fluent API:

 modelBuilder.Entity() .HasRequired(f => f.Bar) .WithMany(b => b.Foos) .HasForeignKey(f => f.BarId); modelBuilder.Entity() .HasKey(fb => new { fb.FooId, fb.BarId }); // replaces the [Key] annotations modelBuilder.Entity() .HasRequired(fb => fb.Foo) .WithMany(f => f.FooBars) .HasForeignKey(fb => fb.FooId); modelBuilder.Entity() .HasRequired(fb => fb.Bar) .WithMany(b => b.FooBars) .HasForeignKey(fb => fb.BarId); 

在数据库模式中, FooBar表将具有复合主键:

 [FooBar] [Foo] [Bar] FooId PK,FK Id PK Id PK BarId PK,FK BarId FK Name IsRead Name Description Description 

但是在FooBar中使用PK是必要的,因为EF模型中的每个实体都必须定义一个键属性 – 单个或复合 – 映射到数据库表中的主键。

在这个问题中 – 首先创建代码,多对多,以及关联表中的其他字段 – 更详细地说明如何使用这种类型的关系。 (有时人们也称它为“ 与有效载荷的多对多关系”( IsRead属性是示例模型中的“有效载荷”),但事实上它并不是多对多的。)