C.J.
C.J.

Reputation: 6869

EF Code first error "Entity Set already defined"

I'm using fluent syntax to configure a M:M relationship between simple Movie and Genre entities. That's working OK.

My problem is that I also wish to expose the link/join table MovieGenre as an entity as well. I realize it's not necessary since it only contains keys, but I wish to have it because it will make some operations easier.

I get this error when I try to configure the link table:

The EntitySet 'MovieGenre1' with schema 'Media' and table 'MovieGenre' was already defined.

I know it has to do w/ me already setting up the M:M relationship but haven't figured out how to make it all work together.

Below is my code:

public class Genre
{
   public int GenreID { get; set;}
   public string Name { get; set; }

   public ICollection<Movie> Movies { get; set; }
}

public class Movie
{
   public int MovieID { get; set; }
   public string Title { get; set; }

   public ICollection<Genre> Genres { get; set; }
}

public class MovieGenre
{
   public int MovieID { get; set; }
   public int GenreID { get; set; }

   public Movie Movie { get; set; }
   public Genre Genre { get; set; }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   //Genre
   modelBuilder.Entity<Genre>()
               .ToTable("Genre", "Media");

   //Movie
   var movieCfg = modelBuilder.Entity<Movie>();
   movieCfg.ToTable("Movie", "Media");
   movieCfg.HasMany(m => m.Genres)
           .WithMany(g => g.Movies)
           .Map(m =>
           {
              m.MapLeftKey("MovieID");
              m.MapRightKey("GenreID");
              m.ToTable("MovieGenre", "Media");
           });

   //MovieGenres
   var movieGenresCfg = modelBuilder.Entity<MovieGenre>();
   movieGenresCfg.ToTable("MovieGenre", "Media");
   movieGenresCfg.HasKey(m => new
   {
      m.MovieID, m.GenreID
   });

   base.OnModelCreating(modelBuilder);
}

Any help would be appreciated. Thank you.

Upvotes: 3

Views: 4916

Answers (1)

Cosmin Onea
Cosmin Onea

Reputation: 2738

What you are trying is not possible. You need to remove your MovieGenre from the mapping.

Do not worry about fetching the entity from the database just to add it to the list of Genres. If you load an entity by id multiple times there is only one call to the DB anyway. As EF gets better and a second level cache is added it will just work and perform well. If you are too imperative there is no magic left to the framework.

And if you think about the domain and the users of your model, most of it is reads anyway so not many updates. On top of it adding a movie to a genres or the other way around could mean many things in which case you should not expose the collections directly but add methods to to implement the behaviours.

One example of behaviour would be "if you add a movie to more than 10 genres then the movie should change the say its category to X" and all sorts of other things. Stop thinking about data only. Domain Driven Design is a good book to read on the subject.

In your case here you can consider the Movie entity as a root aggregate because a Movie is what your users are after and a Movie is what you are going to edit as an administrator. A movie can exist without a Genre, maybe, so genre is just a tag on the movies, a value object, and not very important. Your mappings would become:

    public class Genre
    {
       public int GenreID { get; set;}
       public string Name { get; set; }
    }

    public class Movie
    {
       public int MovieID { get; set; }
       public string Title { get; set; }

       public ICollection<Genre> Genres { get; set; }
    }
....

    //Movie
   var movieCfg = modelBuilder.Entity<Movie>();
   movieCfg.ToTable("Movie", "Media");
   movieCfg.HasMany(m => m.Genres)
           .WithMany()
           .Map(m =>
           {
              m.MapLeftKey("MovieID");
              m.MapRightKey("GenreID");
              m.ToTable("MovieGenre", "Media");
           });

The queries would look like:

// get all movies for a particular genre
var movies = movieContext.Movies.Where(m => m.Genres.Any(g => g.GenreID = id))
                                .OrderBy(m => m.Name)
                                .ToList();

// get all movies 
var movies = movieContext.Movies.OrderBy(m => m.Name).Take(10).ToList();

This is pretty much all you are going to do with your domain.

Upvotes: 9

Related Questions