簡體   English   中英

使用 EF Core 在 ASP.NET Core 應用程序中使用多對多關系將記錄添加到數據庫

[英]Adding records to database with many-to-many relations in ASP.NET Core app with EF Core

您好,出於學習目的,我正在為電影數據庫編寫 ASP.NET Core Web API 應用程序,其圖表如下所示:
數據庫圖 img 鏈接(抱歉,沒有足夠的聲譽來發布圖片)
這是 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 類用於請求是一種很好的做法,因此我為名為MovieCreateMovie實體實現了一個。

    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 類中沒有意義。 另一個區別是導航屬性的類型更改,例如,由於缺少MovieId信息而再次從IList<MovieGenre>更改為IList<int>

所以我試圖找出將數據從 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方法的第一個版本中,例如,我可以將數據添加到MovieGenres DbSet ,而不是將MovieGenre數據添加到導航屬性。 那么哪種方式是“正確”的呢?
PS 如果不是問太多,我真的很感激有關更新(PUT)和 PATCH 請求的任何提示,我認為特別是在 PATCH 上,因為從我在 stackoverflow 上看到的內容來看,它真的很復雜。

這兩個選項顯然都有效,所以我不知道“正確”是否是正確的術語。 但是從代碼和性能的角度來看,第二個選項肯定更有效。 代碼更易於閱讀且更緊湊,並且您只需調用 SaveChanges 一次。 您可以通過刪除連接表的“MovieId = 0”來稍微清理第二個選項。 它由 EF 假定,因此您不需要它。

就個人而言,對於更新,我只是提取現有記錄,然后手動檢查那些可以更新的屬性,進行比較以查看它們是否更改並在需要時更新(一堆 IF 語句)。 通過只接觸實際更新的字段,EF 可以創建更高效​​的 UPDATE 查詢。 如果不需要,不要只是將每個字段從傳入的更新 DTO 自動復制到現有實體。

很抱歉,我不怎么使用 PATCH,所以我不能談論它。

將 ActorIds 和 GenreIds 更改為 int 數組

    public class MovieCreationDto
    {
        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 int[] ActorIds { get; set; }
        public int[] GenreIds { get; set; }
    }

並將您的項目更新到 ASP.NET Core 5 和 EF Core 5 因為在 EF Core 5 中建立多對多關系非常容易,而查詢多對多關系要容易得多。

使用 LINQ 中的 Select(Projection) 更改將演員和流派添加到電影的方式。

EF Core 5.0 的新增功能

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM