I use EF Code First and have a problem in n-n relationship, assume we have a singer that sing in some genres, so we need this models: Artist, Genre, and ArtistsGenres, I def
The connection between Artist and genres is the ArtistsGenre.
So artist containt: ID, Name
And Genre contains: ID, Title
And ArtistsGenre contains: ID artist, ID genre
You haven't to create a separate Model
for association between to models in a many-to-many relationship. Really the ArtistsGenres
is not necessary. So, remove it, and you just have to change your modelBuilder
to this one:
modelBuilder.Entity<Artist>()
.HasMany(c => c.Genres)
.WithMany(x => x.Artists)
.Map(a => {
a.ToTable("ArtistsGenres");
a.MapLeftKey("ArtistId");
a.MapRightKey("GenreId");
});
It will use the ArtistsGenres
table to map a many-to-many relationship between Artists
table and
Genres
table automatically.
Note: When you define the ArtistsGenres
model, EF will not look at it as a relationship,
because you tell him that Hey EF, I have another model named ArtistsGenres
! Please manage it for me!!!
Your new entities and dbcontext will be these:
public class Artist {
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Genre> Genres { get; set; }
}
public class Genre {
public long Id { get; set; }
public string Title { get; set; }
public ICollection<Artist> Artists { get; set; }
}
public class MusicDB : DbContex {
public DbSet<Artist> Artists { get; set; }
public DbSet<Genre> Genres { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Artist>()
.HasMany(c => c.Genres)
.WithMany(x => x.Artists)
.Map(a => {
a.ToTable("ArtistsGenres");
a.MapLeftKey("ArtistId");
a.MapRightKey("GenreId");
});
}
Let me know if you have any questions or need clarifications on any part.
The problem is you are not explicitly loadin the Genres
collection of the artist class and you do not allow EF to intercept that property access by not declaring it as virtual
.
public class Artist
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
}
Then when you need to access artist and the related genres you need to eager load them
var artist = db.Artists.Include(a => a.Genres)
.Where(a => a.Name == "Foo").SingleOrDefault()
Making the Genres
property virtual will allow EF to lazy load the collection if you didn't eager load it.
I would suggest you to go with the simpler approach of creating another model ArtistGenre and let EF figure out the relationship on its own. Create the table as below.
public class ArtistGenre
{
public int Id;
public int GenreId;
public int ArtistId;
public virtual Genre Genre;
public virtual Artist Artist;
}
After that you will have another table added to the database by the above name with two foriegn key properties and one primary key.
Now, you can run the queries on this table. Say
var artist = myContext.ArtistGenre.where( g = g.GenreId == 1).ToList();
Now, artist wil hold all the artist under Genre with Id =1. You can do the vice-versa for Genres too in the similar way.
Hope it helps !!