From examples that I have seen online and in a Programming Entity Framework CodeFirst book, when you have a collection on both classes EF would create a mapping table such as MembersRecipes
and the primary key from each class would link to this table.
However when I do the below, I instead get a new field in the Recipes
table called Member_Id
and a Recipe_Id
in the Members
table.
Which only creates two one-to-many relationships, but not a many-to-many so I could have Member 3 linked to Recipes (4,5,6) and Recipe 4 linked to Members (1,2,3) etc.
Is there a way to create this mapping table? and if so how do you name it something else such as "cookbooks" ?
Thanks
public abstract class Entity { [Required] public int Id { get; set; } } public class Member : Entity { [Required] public string Name { get; set; } public virtual IList<Recipe> Recipes { get; set; } } public class Recipe : Entity { [Required] public string Name { get; set; } [ForeignKey("Author")] public int AuthorId { get; set; } public virtual Member Author { get; set; } .... public virtual IList<Member> Members { get; set; } }
UPDATE: Below is another approach I have tried which doesn't use the Fluent API and replaces the AuthorId
& Author
on Recipe
with an owner flag, I have also renamed the below example from Cookbooks
to MembersRecipes
, this also fixes my issue similar to the answer but as mentioned has further implications.
public class MembersRecipes { [Key, Column(Order = 0)] [ForeignKey("Recipe")] public int RecipeId { get; set; } public virtual Recipe Recipe { get; set; } [Key, Column(Order = 1)] [ForeignKey("Member")] public int MemberId { get; set; } public virtual Member Member { get; set; } public bool Owner { get; set; } }
and in Recipe
& Member
classes I changed the collections to
public virtual IList<MembersRecipes> MembersRecipes { get; set; }
In order to map a many-to-many association, we use the @ManyToMany, @JoinTable and @JoinColumn annotations. Let's have a closer look at them. The @ManyToMany annotation is used in both classes to create the many-to-many relationship between the entities.
A many-to-many relationship occurs when multiple records in a table are associated with multiple records in another table. For example, a many-to-many relationship exists between customers and products: customers can purchase various products, and products can be purchased by many customers.
When you need to establish a many-to-many relationship between two or more tables, the simplest way is to use a Junction Table. A Junction table in a database, also referred to as a Bridge table or Associative Table, bridges the tables together by referencing the primary keys of each data table.
If that sounds abstract, let's look at a specific example: Suppose you have a list of students and a list of classes. There's a many-to-many relationship between the students and their classes, since each student can take multiple classes, and each class can have multiple students enrolled.
Do this on your DbContext OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Recipe>() .HasMany(x => x.Members) .WithMany(x => x.Recipes) .Map(x => { x.ToTable("Cookbooks"); // third table is named Cookbooks x.MapLeftKey("RecipeId"); x.MapRightKey("MemberId"); }); }
You can do it the other way around too, it's the same, just another side of the same coin:
modelBuilder.Entity<Member>() .HasMany(x => x.Recipes) .WithMany(x => x.Members) .Map(x => { x.ToTable("Cookbooks"); // third table is named Cookbooks x.MapLeftKey("MemberId"); x.MapRightKey("RecipeId"); });
Further examples:
http://www.ienablemuch.com/2011/07/using-checkbox-list-on-aspnet-mvc-with_16.html
http://www.ienablemuch.com/2011/07/nhibernate-equivalent-of-entity.html
UPDATE
To prevent cyclical reference on your Author property, aside from above, you need to add this:
modelBuilder.Entity<Recipe>() .HasRequired(x => x.Author) .WithMany() .WillCascadeOnDelete(false);
Idea sourced here: EF Code First with many to many self referencing relationship
The core thing is, you need to inform EF that the Author property(which is a Member instance) has no Recipe collections(denoted by WithMany()
); that way, cyclical reference could be stopped on Author property.
These are the created tables from the Code First mappings above:
CREATE TABLE Members( Id int IDENTITY(1,1) NOT NULL primary key, Name nvarchar(128) NOT NULL ); CREATE TABLE Recipes( Id int IDENTITY(1,1) NOT NULL primary key, Name nvarchar(128) NOT NULL, AuthorId int NOT NULL references Members(Id) ); CREATE TABLE Cookbooks( RecipeId int NOT NULL, MemberId int NOT NULL, constraint pk_Cookbooks primary key(RecipeId,MemberId) );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With