Greeting, for learning purposes I'm writing an ASP.NET Core Web API app for a movie database, diagram of which is shown below:
Database diagram img link (Sorry, not enough reputation to post images)
Here's what Context class looks like
public class MovieDbContext : DbContext
{
public MovieDbContext(DbContextOptions<MovieDbContext> options)
:base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MovieActor>()
.HasKey(ma => new { ma.MovieId, ma.ActorId });
modelBuilder.Entity<MovieGenre>()
.HasKey(mg => new { mg.MovieId, mg.GenreId});
}
public DbSet<Movie> Movies { get; set; }
public DbSet<Actor> Actors { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<MovieActor> MovieActors { get; set; }
public DbSet<MovieGenre> MovieGenres { get; set; }
}
I've read in several resources and stackoverflow posts that it's a good practice to use DTO classes for requests so I've implemented one for the Movie entity called MovieCreate.
public class Movie
{
public int MovieId { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[Required]
public int Year { get; set; }
[Required]
[StringLength(4000)]
public string Description { get; set; }
[Required]
[Column(TypeName = "decimal(3, 1)")]
public decimal Score { get; set; }
[Required]
[StringLength(2000)]
public string ImgUrl { get; set; }
public IList<MovieActor> MovieActors { get; set; }
public IList<MovieGenre> MovieGenres { get; set; }
}
public class MovieCreate
{
public string Name { get; set; }
public int Year { get; set; }
public string Description { get; set; }
public decimal Score { get; set; }
public string ImgUrl { get; set; }
public IList<int> ActorIds { get; set; }
public IList<int> GenreIds { get; set; }
}
The difference between MovieCreate class and Movie entity is the absence of MovieId property because well we don't know the current value of the PK and even more importantly IDENTITY_INSERT parameter is OFF, so no point in having that inside DTO class. Another difference is type change of navigation properties from, for example, IList<MovieGenre> to IList<int> again caused by the lack of MovieId information.
So I'm trying to figure out the correct way to store data from POST request to the database. I have a service inside of DataAccess project that is responsible for operations with database. Here's what Create method in that service looks like:
public async Task<MovieResponse> Create(MovieCreate newMovie)
{
Movie movie = newMovie.MapMovie();
//We save changes to database first so that MovieId is assigned to movie variable
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
foreach (var actor in newMovie.ActorIds)
{
movie.MovieActors.Add(new MovieActor { ActorId = actor, MovieId = movie.MovieId });
}
foreach (var genre in newMovie.GenreIds)
{
movie.MovieGenres.Add(new MovieGenre { GenreId = genre, MovieId = movie.MovieId });
}
_context.Movies.Update(movie);
await _context.SaveChangesAsync();
return movie.MapMovieResponse();
}
I wonder if this is the correct or should I say acceptable way to do things? Also by trial and error and some googling after that I figured that by either explicitly assigning MovieId to 0 or just never assigning it, which would result in it to be default integer value(which is 0 as well), EF would assume that we didn't explicitly specified PK value so it would be generated by database when saved, so code like this also works:
public async Task<MovieResponse> Create(MovieCreate newMovie)
{
Movie movie = newMovie.MapMovie();
//technically movie.MovieId == 0 at this point, since no value was assigned to it, so it's default
foreach (var actor in newMovie.ActorIds)
{
movie.MovieActors.Add(new MovieActor { ActorId = actor, MovieId = 0});
}
foreach (var genre in newMovie.GenreIds)
{
movie.MovieGenres.Add(new MovieGenre { GenreId = genre, MovieId = 0});
}
//And now we save changes to the database
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return movie.MapMovieResponse();
}
Also in the first version of Create method instead of adding, for example, MovieGenre data to the navigation property I could add data to MovieGenres DbSet. So which way is the "correct" one?
P.S. If it's not too much to ask I would really appreciate any tips on Update (PUT) and PATCH requests, I think especially on PATCH since from what I've seen on stackoverflow it's really complicated.