【发布时间】:2023-03-17 00:22:01
【问题描述】:
您好,出于学习目的,我正在为电影数据库编写一个 ASP.NET Core Web API 应用程序,其示意图如下所示:
Database diagram img link(抱歉,没有足够的声望来发布图片)
这是 Context 类的样子
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; }
}
我在一些资源和 stackoverflow 帖子中阅读到,将 DTO 类用于请求是一种很好的做法,因此我为名为 MovieCreate 的 Movie 实体实现了一个。
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; }
}
MovieCreate 类和Movie 实体之间的区别在于缺少MovieId 属性,因为我们不知道 PK 的当前值,更重要的是 IDENTITY_INSERT 参数是 OFF,所以没有意义在 DTO 类里面。另一个区别是导航属性的类型从IList<MovieGenre> 更改为IList<int> 也是由于缺少MovieId 信息引起的。
所以我试图找出将数据从 POST 请求存储到数据库的正确方法。我在 DataAccess 项目中有一项服务,负责对数据库进行操作。该服务中的 Create 方法如下所示:
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();
}
我想知道这是正确的还是我应该说可以接受的做事方式?同样通过反复试验和一些谷歌搜索,我发现要么明确地将MovieId 分配给 0,要么永远不分配它,这将导致它成为默认整数值(也是 0),EF 会假设我们没有明确指定 PK 值,所以它会在保存时由数据库生成,所以这样的代码也可以:
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();
}
同样在Create 方法的第一个版本中,我可以将数据添加到MovieGenresDbSet,而不是添加例如MovieGenre 数据到导航属性。那么哪种方式是“正确”的方式?
附言如果问得不算太多,我真的很感谢有关更新(PUT)和 PATCH 请求的任何提示,我认为尤其是在 PATCH 上,因为从我在 stackoverflow 上看到的情况来看,它真的很复杂。
【问题讨论】:
标签: c# .net asp.net-core .net-core entity-framework-core