EF Core 6插入MovieGenre连接表报错,请求排查解决
EF Core 6.07插入连接表MovieGenre报错排查与解决
错误原因分析
报错InvalidOperationException: 尝试保存更改时,'MovieGenre.MovieId'的值未知。这是因为该属性同时属于一个外键,而该关系中的主体实体未知。主要由两个问题导致:
1. 视图模型绑定错误
Create.cshtml存在多处绑定错误:
- 模型声明错误:
@model GenreModel应为@model MovieGenreModel - 输入框name属性错误:两个输入框的name均为"Name",无法映射到MovieGenreModel的MovieId和GenreId字段
- 标签、id与实际字段不匹配:标签显示GenreId/MovieId,但输入框id和占位文本均指向"GenreName"
2. EF上下文实体状态误判
直接创建MovieGenre实体并调用AddAsync时,EF Core会默认将关联的Movies和Genre实体标记为Added状态(即使仅设置了外键ID),但这些实体已存在于数据库中,导致EF无法识别主体实体,触发外键验证错误。
解决步骤
步骤1:修复Create.cshtml视图
修正模型绑定与输入框属性:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @model Antra.Movie.Core.Model.MovieGenreModel <form method="post" asp-controller="MovieGenre" asp-action="Create"> <div class="form-group"> <label for="GenreId">GenreId</label> <input type="number" name="GenreId" class="form-control" id="GenreId" placeholder="输入GenreId"> <span asp-validation-for="GenreId"></span> </div> <div class="form-group"> <label for="MovieId">MovieId</label> <input type="number" name="MovieId" class="form-control" id="MovieId" placeholder="输入MovieId"> <span asp-validation-for="MovieId"></span> </div> <button type="submit" class="btn btn-primary">Save</button> </form>
步骤2:修复插入逻辑(二选一)
方案A:附加主体实体到上下文
在MovieGenreRepositoryAsync的InsertAsync方法中,先将Movies和Genre实体附加到上下文并标记为Unchanged,告诉EF这些实体已存在:
public async Task<int> InsertAsync(MovieGenre entity) { // 附加Movie实体,标记为已存在 db.Attach(new Movies { MovieId = entity.MovieId }).State = EntityState.Unchanged; // 附加Genre实体,标记为已存在 db.Attach(new Genre { GenreId = entity.GenreId }).State = EntityState.Unchanged; await db.Set<MovieGenre>().AddAsync(entity); return await db.SaveChangesAsync(); }
方案B:直接设置连接表实体状态为Added
确认MovieId和GenreId已存在的前提下,直接将MovieGenre实体状态设为Added,避免EF递归处理关联实体:
public async Task<int> InsertAsync(MovieGenre entity) { db.Entry(entity).State = EntityState.Added; return await db.SaveChangesAsync(); }
步骤3:优化视图(可选)
为避免手动输入ID出错,在Create视图中使用下拉框选择已有Movie和Genre:
- 修改MovieGenreController的Create Get方法:
[HttpGet] public async Task<IActionResult> Create() { ViewBag.MovieList = new SelectList(await movieServiceAsync.GetAllMovies(), "MovieId", "Title"); ViewBag.GenreList = new SelectList(await genreServiceAsync.GetAllGenres(), "GenreId", "Name"); return View(); }
- 替换视图中的输入框为下拉框:
<div class="form-group"> <label for="GenreId">Genre</label> <select name="GenreId" class="form-control" id="GenreId"> <option value="">选择Genre</option> @foreach (var item in ViewBag.GenreList) { <option value="@item.Value">@item.Text</option> } </select> <span asp-validation-for="GenreId"></span> </div> <div class="form-group"> <label for="MovieId">Movie</label> <select name="MovieId" class="form-control" id="MovieId"> <option value="">选择Movie</option> @foreach (var item in ViewBag.MovieList) { <option value="@item.Value">@item.Text</option> } </select> <span asp-validation-for="MovieId"></span> </div>
内容的提问来源于stack exchange,提问作者nunya
相关产品推荐
相关产品推荐

