6

This should be a simple question for the well versed EF user.

I have the following schema (in my head) of how the relationships between the tables should look.

[FooBar]      [Foo]          [Bar]

FooId PK,FK   Id PK          Id PK
BarId PK,FK   BarId FK       Name
IsRead        Name           Description
              Description    

Though, when I try to generate the schema using EF code-first it fails to interpret the relationships between the entities as I've interpreted them (adds foreign key FooId to the [bar] table) and fails to fully create the [FooBar] bridge table.

If someone could guide me on how to achieve the above schema using EF4 code-first I'd appreciate it. Whether the solution involves attributes on my POCO models, fluent configurations or a hybrid of both doesn't matter much - as long as the desired database schema is created.


POCO Models:

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<Bar> BridgedBars { get; set; }

    public Foo()
    {
        Bars = new List<Bar>();
    }
}

public class Bar
{
    public int Id { get; set; }
    public string Text { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Foo> Foos { get; set; }
    public virtual ICollection<Foo> BridgedFoos { get; set; }

    public Bar()
    {
        Foos = new List<Foo>();
        BridgedFoos = new List<Foo>();
    }
}

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; }
}
4

1 回答 1

9

您的模型确实会FooIdBar属于定义的关系中创建一个外键Foo.BrideBars。EF 不会将此导航属性与其中的一个ICollection<Foo>属性相关联,Bar因为其中有两个,并且 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<FooBar> FooBars { get; set; }
}

public class Bar
{
    public int Id { get; set; }
    public string Text { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Foo> Foos { get; set; }
    public virtual ICollection<FooBar> 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

我猜,您真正的类和属性没有名称Fooand Bar。如果您的真实姓名不遵循约定,您可能必须使用注释或 Fluent API 指定关系:

modelBuilder.Entity<Foo>()
    .HasRequired(f => f.Bar)
    .WithMany(b => b.Foos)
    .HasForeignKey(f => f.BarId);

modelBuilder.Entity<FooBar>()
    .HasKey(fb => new { fb.FooId, fb.BarId }); // replaces the [Key] annotations

modelBuilder.Entity<FooBar>()
    .HasRequired(fb => fb.Foo)
    .WithMany(f => f.FooBars)
    .HasForeignKey(fb => fb.FooId);

modelBuilder.Entity<FooBar>()
    .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    

但是有一个 PKFooBar是必要的,因为 EF 模型中的每个实体都必须定义一个键属性 - 单个或复合 - 它映射到数据库表中的主键。

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

于 2012-06-03T12:06:48.120 回答