【问题标题】:One to many relationship with junction table using Entity Framework code first首先使用实体​​框架代码与联结表的一对多关系
【发布时间】:2015-10-06 12:48:42
【问题描述】:

我首先使用 EF6 代码创建数据库,并且正在努力使用联结表创建一对多关系。

这是我正在尝试做的一个示例:

Foo 实体可以包含任意数量 (0-n) 个 Bar 实体,但 Bar 实体不一定属于 Foo。我可能希望不同类型的实体也包含一个或多个Bar,因此Bar 不包含其父级的外键很重要。

所以连接表应该是这样的:

Name        | FooBar
------------|-------
Primary Key | BarID
Key         | FooID

所以如果我们创建实体如下:

public class Foo
{
  public long ID { get; set; }
  public ICollection<Bar> Bars { get; set; }
}

public class Bar
{
  public long ID { get; set; }
}

然后配置它们:

public class FooConfiguration : EntityTypeConfiguration<Foo>
{
  HasKey(p => p.ID);
  HasMany(p => p.Bars)
    .WithRequired()
    .Map(m => {
      m.ToTable("FooBar");
      m.MapKey("FooKey");
    });
}

但这会导致抛出以下异常:

An exception of type 'System.InvalidOperationException' occurred in mscorlib.dll but was not handled in user code. Additional information: The specified table 'FooBar' was not found in the model. Ensure that the table name has been correctly specified.

很遗憾,我不确定这意味着什么 - 我需要创建一个单独的 FooBar 实体吗?

如何配置这些实体以便正确创建连接表?

谢谢!

【问题讨论】:

  • the child entity belongs to many different parent entities 如果父母可能有多个孩子,那么您处于多对多关系中,并且 afaik 这是您唯一需要/在 EF 中有联结表的案例
  • 我应该更清楚一点 - 子实体被许多不同的实体使用,但这具体是我正在努力创建的一对多子关系。
  • 您的意思是孩子可能有也可能没有单亲,但可能与许多类型为 Parent 的实体有(另一个)关系?
  • 我用实体名称“父母”和“孩子”混淆了,抱歉。父母可以有 0-n 个孩子,孩子有 1 个父母。但是,我不希望将父级的外键存储在子级上,因为其他实体也有这些子级。我将更改原始问题中实体的名称,以避免混淆父母和孩子的含义。
  • 联结表应该至少有 2 列并且仅用于 many-many 关系。所以你需要把这里的关系当作many-many而不是one-many。尝试用 WithMany() 替换 WithRequired 并为联结表映射 2 个键(而不是像您所做的那样只有 1 个)。

标签: entity-framework ef-code-first entity-framework-6 one-to-many


【解决方案1】:

很抱歉复活这个线程,但我想分享一个我创建的实用程序来处理一个 WebApi,它有许多相同类型的关系实例。我们有多个业务对象,每个都与 EventHistory、消息、文件等有关系,跟踪所有 FluentAPI 函数很麻烦。这是我想出的:

    /// <summary>
    /// Maps a many-to-many relationship that can only be navigated from TSource to TTarget.
    /// </summary>
    /// <typeparam name="TSource">Source type that can navigate to TTarget.</typeparam>
    /// <typeparam name="TTarget">Target type that has no direct link back to TSource.</typeparam>
    /// <param name="modelBuilder">An instance of DbModelBuilder</param>
    /// <param name="expr">Lambda expression specifying the navigation property for this relationship from TSource to TTarget.</param>
    /// <param name="leftKey">Optional argument to override the foreign key to TSource</param>
    /// <param name="rightKey">Optional argument to override the foreign key to TTarget</param>
    public static void MapDirectionalManyToMany<TSource, TTarget>(DbModelBuilder modelBuilder, Expression<Func<TSource, ICollection<TTarget>>> expr, string tableName = null, string leftKey = null, string rightKey = null)
        where TSource : class
        where TTarget : class {

        modelBuilder.Entity<TSource>()
            .HasMany(expr)
            .WithMany()
            .Map(m => {
                m.MapLeftKey(leftKey ?? typeof(TSource).Name + "Id");
                m.MapRightKey(rightKey ?? typeof(TTarget).Name + "Id");
                m.ToTable(tableName ?? typeof(TSource).Name + typeof(TTarget).Name);
            });

    }

我这样应用这种关系:

ModelUtils.MapDirectionalManyToMany<MyResourceModel, SharedHistoryModel>(modelBuilder, x => x.History);
ModelUtils.MapDirectionalManyToMany<OtherResourceModel, SharedHistoryModel>(modelBuilder, x => x.History, "renamed_history");

【讨论】:

    【解决方案2】:

    如果您不想将foo 的密钥存储在您的bar 中,那么您必须使用多对多关系。使其表现得像一对多关系的方式取决于您之后的实现,仍然有一种方法可以确保 bar 只有一个 foo ,但是对于 EF6 来说,制作联结表是唯一的方法to go是多对多的关系。

    【讨论】:

    • 谢谢,将其配置为多对多关系确实符合我的预期,并且出于我的目的,它不会导致任何直接问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 2011-07-29
    相关资源
    最近更新 更多