如何在Razor Pages的C#多对多模型中强制实体参与?
问题:Razor Pages中强制Actor与Movie多对多关系的双向参与要求
我正在Razor Pages中使用C#建立Actor模型(actor.cs)与Movie模型(movie.cs)的多对多(M:N)关系,已创建中间表ActorMovie.cs,包含对应的外键(FK)及指向这两个模型的导航属性,三个模型均已生成CRUD页面。我希望强制双方实体的参与:即一个演员必须参演至少一部电影,一部电影必须至少有一个演员参演。目前用户需要进入中间表ActorMovie的创建页面来关联演员与电影,无法实现所需的实体参与强制要求,请问该如何实现?
我已运行应用并通过各自的创建页面创建了演员和电影,但必须进入中间表的创建页面来关联双方,我希望在创建实体时就强制这种关联。
现有模型代码
Actor模型
public class Actor { public int actorID { get; set; } public string? actorName { get; set; } public double actorRating { get; set; } public int actorMovieCount { get; set; } public double actorEXP { get; set; } public List<ActorMovie>? ActorMovies { get; set; } }
Movie模型
public class Movie { public int Id { get; set; } public string? Title { get; set; } [Display(Name = "Release Date")] [DataType(DataType.Date)] public DateTime ReleaseDate { get; set; } public string? Genre { get; set; } [Column(TypeName = "decimal(18, 2)")] public decimal Price { get; set; } public string Rating { get; set; } = string.Empty; public List<ActorMovie>? ActorMovies { get; set; } }
中间表ActorMovie模型
public class ActorMovie { public int ID { get; set; } public Actor? Actor { get; set; } public int? actorID { get; set; } public Movie? Movie { get; set; } public int? MovieID { get; set; } }
解决方案
第一步:修正模型约束,确保中间表关联有效性
首先要保证中间表的外键不能为空,避免出现无关联的中间记录,同时明确导航属性的非空约束:
public class ActorMovie { public int ID { get; set; } // 移除可空标记,确保必须关联存在的Actor和Movie public Actor Actor { get; set; } = null!; public int actorID { get; set; } public Movie Movie { get; set; } = null!; public int MovieID { get; set; } }
同时,更新Actor和Movie的导航属性为非空初始化,避免空引用:
// Actor模型修改 public List<ActorMovie> ActorMovies { get; set; } = new(); // Movie模型修改 public List<ActorMovie> ActorMovies { get; set; } = new();
第二步:修改创建页面,强制关联选择
1. 修改Actor的Create页面
前端(Create.cshtml)
添加电影多选下拉框,强制用户选择至少一部电影:
<div class="form-group"> <label class="control-label">关联电影(至少选择一部)</label> <select asp-for="SelectedMovieIds" asp-items="Model.MovieSelectList" multiple class="form-control" required> <option value="">请选择电影</option> </select> <span asp-validation-for="SelectedMovieIds" class="text-danger"></span> </div>
后端(Create.cshtml.cs)
添加选择列表和绑定属性,在提交时验证并创建关联:
private readonly YourDbContext _context; public CreateModel(YourDbContext context) { _context = context; } [BindProperty] public Actor Actor { get; set; } = null!; [BindProperty] public List<int> SelectedMovieIds { get; set; } = new(); public SelectList MovieSelectList { get; set; } = null!; public async Task OnGetAsync() { var movies = await _context.Movies.ToListAsync(); MovieSelectList = new SelectList(movies, "Id", "Title"); } public async Task<IActionResult> OnPostAsync() { // 验证:必须选择至少一部电影 if (!ModelState.IsValid || SelectedMovieIds.Count == 0) { if (SelectedMovieIds.Count == 0) { ModelState.AddModelError("SelectedMovieIds", "必须选择至少一部电影"); } // 重新加载电影列表,返回页面 var movies = await _context.Movies.ToListAsync(); MovieSelectList = new SelectList(movies, "Id", "Title"); return Page(); } // 保存演员 _context.Actors.Add(Actor); await _context.SaveChangesAsync(); // 创建关联记录 foreach (var movieId in SelectedMovieIds) { _context.ActorMovies.Add(new ActorMovie { actorID = Actor.actorID, MovieID = movieId }); } await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
2. 修改Movie的Create页面
前端(Create.cshtml)
添加演员多选下拉框,强制用户选择至少一个演员:
<div class="form-group"> <label class="control-label">关联演员(至少选择一个)</label> <select asp-for="SelectedActorIds" asp-items="Model.ActorSelectList" multiple class="form-control" required> <option value="">请选择演员</option> </select> <span asp-validation-for="SelectedActorIds" class="text-danger"></span> </div>
后端(Create.cshtml.cs)
类似Actor的逻辑,验证并创建关联:
private readonly YourDbContext _context; public CreateModel(YourDbContext context) { _context = context; } [BindProperty] public Movie Movie { get; set; } = null!; [BindProperty] public List<int> SelectedActorIds { get; set; } = new(); public SelectList ActorSelectList { get; set; } = null!; public async Task OnGetAsync() { var actors = await _context.Actors.ToListAsync(); ActorSelectList = new SelectList(actors, "actorID", "actorName"); } public async Task<IActionResult> OnPostAsync() { // 验证:必须选择至少一个演员 if (!ModelState.IsValid || SelectedActorIds.Count == 0) { if (SelectedActorIds.Count == 0) { ModelState.AddModelError("SelectedActorIds", "必须选择至少一个演员"); } // 重新加载演员列表,返回页面 var actors = await _context.Actors.ToListAsync(); ActorSelectList = new SelectList(actors, "actorID", "actorName"); return Page(); } // 保存电影 _context.Movies.Add(Movie); await _context.SaveChangesAsync(); // 创建关联记录 foreach (var actorId in SelectedActorIds) { _context.ActorMovies.Add(new ActorMovie { actorID = actorId, MovieID = Movie.Id }); } await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
第三步:补充编辑页面的验证逻辑
在编辑Actor或Movie时,也要确保关联的记录至少保留一个,比如编辑Actor时:
public async Task<IActionResult> OnPostAsync(int id) { var actorToUpdate = await _context.Actors .Include(a => a.ActorMovies) .FirstOrDefaultAsync(a => a.actorID == id); if (actorToUpdate == null) { return NotFound(); } if (await TryUpdateModelAsync<Actor>( actorToUpdate, "Actor", a => a.actorName, a => a.actorRating, a => a.actorMovieCount, a => a.actorEXP)) { // 检查关联的电影数量,为0则阻止保存 if (actorToUpdate.ActorMovies.Count == 0) { ModelState.AddModelError(string.Empty, "演员必须至少关联一部电影"); // 重新加载电影选择列表 var movies = await _context.Movies.ToListAsync(); MovieSelectList = new SelectList(movies, "Id", "Title"); return Page(); } await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } // 加载选择列表,返回页面 var moviesList = await _context.Movies.ToListAsync(); MovieSelectList = new SelectList(moviesList, "Id", "Title"); return Page(); }
Movie的编辑页面可以参考上述逻辑,确保关联的演员数量不为0。
注意事项
数据库层面无法直接强制Actor和Movie必须有关联(会导致循环依赖约束,数据库无法创建),因此所有强制逻辑都需要在业务代码中实现,包括创建、编辑、删除操作时的验证。
内容的提问来源于stack exchange,提问作者NewUser5055
相关产品推荐
相关产品推荐

