You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 21:15:01