首先使用实体​​框架代码与联结表建立一对多关系

时间:2015-10-06 12:48:43

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

我首先使用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实体?

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

谢谢!

2 个答案:

答案 0 :(得分:3)

很抱歉要复活这个帖子,但我想分享一个我创建的实用程序来处理一个WebApi,它有许多这种关系的实例。我们有多个业务对象,每个对象都与EventHistory,Messages,Files等有关系,跟踪所有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");

答案 1 :(得分:2)

如果您不想将foo的密钥存储在bar中,那么您必须寻求多对多的关系。您将其视为一对多关系的方式取决于您的实现,之后仍有一种方法可以确保bar只有一个foo,但是对于EF6来说,要建立联结表,唯一的出路就是多对多的关系。