Entity Framework 7多对多关联表插入无数据问题求助
多对多关系下插入Novel记录时关联Genre失败的问题
问题背景
数据库中Novel与Genre通过中间表Novel_Genre形成多对多关系,所有表的ID均为自增主键。采用数据库优先模式,通过Scaffold命令生成实体类,未使用迁移更新数据库。当前插入Novel记录时,Novel表能成功新增数据,但Novel_Genre关联表无对应记录。
现有代码
控制器Create方法
public async Task<IActionResult> Create(CreateNovelViewModel novelVM) { Novel novel = new Novel(); try { if (ModelState.IsValid) { novel.NovelTitle = novelVM.NovelTitle; novel.NovelCover = ""; novel.NovelDescription = novelVM.NovelDescription; novel.AuthorId = novelVM.AuthorID; novel.UserId = novelVM.UserID; foreach(var genreId in novelVM.Genres) { var genre = new Genre(); novel.Genres.Add(genreId); } _context.Add(novel); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } else { ModelState.AddModelError("", "Không thể thêm.Hãy thử lại, nếu vấn đề còn tồn tại, liên hệ người có chuyên môn."); } } catch (RetryLimitExceededException ex) { ModelState.AddModelError("", "Không thể thêm. Hãy thử lại, nếu vấn đề còn tồn tại, liên hệ người có chuyên môn."); } ViewBag.AuthorId = new SelectList(_context.Authors, "AuthorId", "AuthorName", novelVM.AuthorID); ViewBag.UserId = new SelectList(_context.Users, "UserId", "UserId", novelVM.UserID); ViewBag.GenreId = new SelectList(_context.Genres, "GenreId", "GenreName"); return View(novel); }
实体类
Novel.cs
public partial class Novel { [Key] public int NovelId { get; set; } public string NovelTitle { get; set; } = null!; public string NovelDescription { get; set; } = null!; public string NovelCover { get; set; } = null!; public DateTime NovelDatePost { get; set; } = DateTime.Now; public int NovelView { get; set; } = 1; public int AuthorId { get; set; } public string UserId { get; set; } = null!; public virtual Author Author { get; set; } = null!; public virtual ICollection<Chapter> Chapters { get; } = new List<Chapter>(); public virtual User User { get; set; } = null!; public virtual ICollection<UserComment> UserComments { get; } = new List<UserComment>(); public virtual ICollection<UserRating> UserRatings { get; } = new List<UserRating>(); public virtual ICollection<Genre> Genres { get; } = new List<Genre>(); }
Genre.cs
public partial class Genre { public int GenreId { get; set; } public string GenreName { get; set; } = null!; public string GenreDescription { get; set; } = null!; public virtual ICollection<Novel> Novels { get; } = new List<Novel>(); }
视图模型CreateNovelViewModel.cs
public class CreateNovelViewModel { [Required] public String NovelTitle { get; set; } [Required] public String NovelDescription { get; set; } //[Required] //public string NovelCover { get; set; } [Required] public string UserID { get; set; } [Required] public int AuthorID { get; set; } [Required] public ICollection<Genre> Genres { get; set; } }
问题分析与解决方法
核心错误点
- 视图模型设计不合理:
CreateNovelViewModel中的Genres定义为ICollection<Genre>,但前端表单实际提交的是选中的流派ID,而非完整的Genre对象,导致接收的数据无法正确匹配。 - 关联逻辑错误:循环中尝试添加未被上下文追踪的Genre对象(或错误的类型)到Novel的集合中,EF无法识别这些是已存在的实体,因此不会生成关联表记录。
修正步骤
1. 修改视图模型
将接收流派的属性改为ID列表,适配表单提交逻辑:
public class CreateNovelViewModel { [Required] public String NovelTitle { get; set; } [Required] public String NovelDescription { get; set; } [Required] public string UserID { get; set; } [Required] public int AuthorID { get; set; } [Required] public List<int> SelectedGenreIds { get; set; } // 改为接收选中的流派ID列表 }
2. 修正控制器关联逻辑
从数据库查询已存在的Genre实体,添加到Novel的Genres集合中,确保EF能追踪这些实体:
public async Task<IActionResult> Create(CreateNovelViewModel novelVM) { Novel novel = new Novel(); try { if (ModelState.IsValid) { novel.NovelTitle = novelVM.NovelTitle; novel.NovelCover = ""; novel.NovelDescription = novelVM.NovelDescription; novel.AuthorId = novelVM.AuthorID; novel.UserId = novelVM.UserID; // 从数据库获取选中的流派实体 var selectedGenres = await _context.Genres .Where(g => novelVM.SelectedGenreIds.Contains(g.GenreId)) .ToListAsync(); foreach(var genre in selectedGenres) { novel.Genres.Add(genre); } _context.Add(novel); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } else { ModelState.AddModelError("", "Không thể thêm.Hãy thử lại, nếu vấn đề còn tồn tại, liên hệ người có chuyên môn."); } } catch (RetryLimitExceededException ex) { ModelState.AddModelError("", "Không thể thêm. Hãy thử lại, nếu vấn đề còn tồn tại, liên hệ người có chuyên môn."); } ViewBag.AuthorId = new SelectList(_context.Authors, "AuthorId", "AuthorName", novelVM.AuthorID); ViewBag.UserId = new SelectList(_context.Users, "UserId", "UserId", novelVM.UserID); ViewBag.GenreId = new SelectList(_context.Genres, "GenreId", "GenreName"); return View(novel); }
3. 修正前端表单
确保表单提交选中的流派ID,示例如下:
<div class="form-group"> <label asp-for="SelectedGenreIds" class="control-label">流派</label> <select asp-for="SelectedGenreIds" asp-items="ViewBag.GenreId" multiple="multiple" class="form-control"></select> <span asp-validation-for="SelectedGenreIds" class="text-danger"></span> </div>
关键说明
EF处理多对多关系时,必须关联已被上下文追踪的实体(即从数据库查询出来的对象),而非新建未追踪的对象。当将已存在的Genre实体添加到Novel的Genres集合后,EF会在调用SaveChangesAsync时自动生成Novel_Genre关联表的记录,无需手动操作中间表。
内容的提问来源于stack exchange,提问作者pilog
相关产品推荐
相关产品推荐

