Search code examples
c#databaseasp.net-web-apientity-framework-corerelationship

How can I fix a many to many relationship between two entities so that the third table is filled automatically in entity framework and web API (C#)


So I have a Customer entity and a Movie entity and I configured the many to many relationship correctly and EF created the third table CustomerMovie with two FK's being CustomerId and MovieId both being the primary key. so now when I started creating the endpoints and it was all fine until I got to the AddMovie endpoint which adds a new movie to the database, it works but the table CustomerMovie is empty while it should have the CustomerId of the customer who added the movie and the MovieId of the movie that was added, so not sure if the endpoint implementation is wrong so far as to reach this goal or is the configuration wrong.

This my endpoint implementation

 [HttpPost("AddMovie"), Authorize]
 public ActionResult<Movie> AddMovie([FromBody] MovieDto movie)
 {
     if (movie == null || !ModelState.IsValid)
     {
         return BadRequest(ModelState);
     }

     if (_movieRepository.CheckMovieByTitle(movie.Title))
     {
         ModelState.AddModelError("", "Movie Already exists, if both share the same name add the release year with the name");
         return StatusCode(422, ModelState);
     }
     var userEmail = User.FindFirstValue(ClaimTypes.Email);
     Customer customerAdded = _customerRepository.GetCustomerByEmail(userEmail);
     Movie addedMovie = _movieRepository.AddMovie(movie, userEmail, customerAdded);
     
     return Ok(addedMovie);
 }

and this is what's in the repository class(I'm using repository pattern)

 public Movie AddMovie(MovieDto movie, string email, Customer customerAdded)
 {
     Movie newMovie = new Movie

     {
         Title = movie.Title,
         Description = movie.Description,
         Duration = movie.Duration,
         ReleaseDate = movie.ReleaseDate,
         Rating = 0,
         AddedByUser = email
     };

     _context.Movie.Add(newMovie);

     Save();
     return newMovie;
 }

as for the configuration of the relationship this the context:

 public class ClassContextDb : DbContext
 {
     public ClassContextDb(DbContextOptions<ClassContextDb> options) : base(options)
     {
     }
     public DbSet<Customer> Customer { get; set; }
     public DbSet<Movie> Movie { get; set; }
     public DbSet<Genre> Genre { get; set; }

     public DbSet<MembershipType> MembershipType { get; set; }

     protected override void OnModelCreating(ModelBuilder modelBuilder)
     {

         base.OnModelCreating(modelBuilder);

         modelBuilder.Entity<Customer>()
          .HasMany(c => c.Movie)
          .WithMany(m => m.Customer)
          .UsingEntity(j => j.ToTable("CustomerMovie"));
             


     }
 }

and these are the properties in the models themselves:

  public virtual ICollection<Movie> Movie { get; set; } = new List<Movie>(); //in Customer class
  public virtual ICollection<Customer> Customer { get; set; } = new List<Customer>(); //  in Movie class

and this is the Movie class for more context

public class Movie
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int MovieId { get; set; }

    [Required]
    [MaxLength(30)]
    public string Title { get; set; }

    [Required]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [Range(0, 10)]
    public float Rating { get; set; }

    [Required]
    [MaxLength(200)]
    [MinLength(15)]
    public string Description { get; set; }

    [Required]
    public int Duration { get; set; }
    public string AddedByUser { get; set; }
    public virtual ICollection<Customer> Customer { get; set; } = new List<Customer>();
    public virtual ICollection<Genre> Genre { get; set; }
}

Solution

  • You are missing newMovie.Customer.Add(customerAdded) before calling _context.Movie.Add(newMovie);

    Following is a simplified example:

    Entities

        public class Post
        {
            public int Id { get; set; }
            public string Title { get; set; }
            public ICollection<Tag> Tags { get; } = [];
        }
    
        public class Tag
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public ICollection<Post> Posts { get; } = [];
        }
    

    DB Context

    public class DataContext(DbContextOptions<DataContext> options) : IdentityDbContext(options)
    {
        public DbSet<Post> Posts { get; set; }
        public DbSet<Tag> Tags { get; set; }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
    
            builder.Entity<Post>()
            .HasMany(e => e.Tags)
            .WithMany(e => e.Posts)
            .UsingEntity(
                "PostTag",
                l => l.HasOne(typeof(Tag)).WithMany().HasForeignKey("TagsId").HasPrincipalKey(nameof(Tag.Id)),
                r => r.HasOne(typeof(Post)).WithMany().HasForeignKey("PostsId").HasPrincipalKey(nameof(Post.Id)),
                j => j.HasKey("PostsId", "TagsId"));
        }
    }
    

    Save Entities

    var tag = new Tag { Id = 1, Name = "EF Core Tutorial" };
    context.Tags.Add(tag);
    await context.SaveChangesAsync();
    
    var post = new Post { Id = 1, Title = "How to do Many-To-Many relationship" };
    post.Tags.Add(tag);
    
    context.Posts.Add(post);
    
    await context.SaveChangesAsync();
    
    var posts = context.Posts.First();
    var tags = context.Tags.First();
    

    Debug