One to many relationship with junction table using Entity Framework code first

ef-code-first entity-framework entity-framework-6 one-to-many

Question

I'm creating a database using EF6 code first, and am struggling to create a one-to-many relationship using a junction table.

Here's an example of what I'm trying to do:

Foo entities can contain any number (0-n) of Bar entities, however Bar entities don't necessarily belong to a Foo. I may want a different type of entity to also contain one or more Bar, so it's important that Bar doesn't contain its parent's foreign key.

So the join table would be like so:

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

So if we create the entities as follows:

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

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

And then configure them:

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

But this results in the following exception being thrown:

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.

Unfortunately I'm not sure what this means - do I need to create a seperate FooBar entity?

How can I configure these entities so that the join table is correctly created?

Thanks!

1
2
10/6/2015 1:15:50 PM

Accepted Answer

If you do not want to store the key of foo in your bar then you have to go for a many to many relationship. The way you will make it act like a one to many relationship is up to your implementation after, there is still a way to make sure bar has only one foo , but for EF6 to make the junction table, the only way to go is many to many relationship.

2
10/6/2015 1:25:20 PM

Popular Answer

Sorry to resurrect this thread, but I wanted to share a utility I created to handle a WebApi that had many instances of this same kind of relationship. We have multiple business objects which each have relationships to EventHistory, Messages, Files, etc, and it was messy keeping track of all the FluentAPI functions. Here's what I came up with:

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

    }

And I apply the relationship like so:

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


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow