数据存储到 ASP.NET Core 5 MVC 中关联的 table

Data storing to the associated table in ASP.NET Core 5 MVC

我有三个 table 'Movie'、'Genre' 并且它们关联 table 'MovieGenre'。我要做的是将 Movie table 的值与 Genre table 的值结合起来,并显示 Genre table 的 Name 值与 Movie table 的值,使用第三个关联table 电影类型。

public partial class Movie
{
    public int MovieId { get; set; }
    [Required]
    public string Title { get; set; }
    public string Description { get; set; }
    public string Storyline { get; set; }    
    public int? Year { get; set; }
    [DataType(DataType.Date)]
    [Validators(ErrorMessage = "Date must be after or equal to current date")]
    [Display(Name = "Release Date")]
    public DateTime? ReleaseDate { get; set; }
    public int? Runtime { get; set; }
    [Display(Name = "Movie Type")]
    [Column(TypeName = "nvarchar(20)")]
    public MovieType MovieType { get; set; }
    public ICollection<Genre> Genres { get; set; }

}

public class Genre
{
    [Key]
    public int GenreId { get; set; }       
    [Display(Name="Genre name")]
    public string Name { get; set; }
    public ICollection<Movie> Movies { get; set; }
}

public  class MovieGenre
{
    public int MovieGenreId { get; set; }

    public int MovieId { get; set; }
    public Movie Movie { get; set; }

    public int GenreId { get; set; }
    public Genre Genre { get; set; }       
}

这是此内容的上下文页面

public partial class MovieContext : DbContext
{    
    public MovieContext(DbContextOptions<MovieContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Movie> Movies { get; set; }
    public virtual DbSet<Genre> Genre { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");

        modelBuilder.Entity<MovieGenre>().HasKey(mg => new { mg.MovieId, mg.GenreId });

       modelBuilder.Entity<Movie>()
                  .HasMany(p => p.Genres)
                  .WithMany(p => p.Movies)
                 .UsingEntity(j => j.ToTable("MovieGenre"));

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

我使用了数据库优先的方法。我已经创建了所有其他部分,当我从数据库向 table 输入值时它工作正常。但是我想要实现的是在创建新电影时将外键值 'MovieId' 和 'GenreId' 存储到 'MovieGenre' table.

这是创建操作方法中的代码。如何将 'MovieId' 和 'GenreId' 存储到此代码中的 'MovieGenre' table?

public async Task<IActionResult> Create([Bind("Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType")] Movie movies)
{
        if (ModelState.IsValid)
        {        
            _context.Add(movies);             
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
       
        ViewBag.GenreId = new MultiSelectList(_context.Genre.ToList(), "GenreId", "Name");
        return View(movies);
}

下面是创建 Action 视图代码,这里我使用了 Genre 的复选框,我也想以某种方式将 GenreId 从这里输入到关联 table。

<div class="row">
    <div class="col-md-4 center">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
           
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Description" class="control-label"></label>
                <input asp-for="Description" class="form-control" />
                <span asp-validation-for="Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Storyline" class="control-label"></label>
                <input asp-for="Storyline" class="form-control" />
                <span asp-validation-for="Storyline" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Year" class="control-label"></label>
                <input asp-for="Year" class="form-control" />
                <span asp-validation-for="Year" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Runtime" class="control-label"></label>
                <input asp-for="Runtime" class="form-control" />
                <span asp-validation-for="Runtime" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MovieType" class="control-label"></label>
                <select asp-for="MovieType" class="form-control" asp-items="Html.GetEnumSelectList<MovieType>()"></select>
                <span asp-validation-for="MovieType" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label class="control-label">Movie Genre</label>
                <div class="col-md-10">
                    <div class="checkbox">

                     
                        @foreach (var item in (MultiSelectList)ViewBag.GenreId)
                        {
                            <input type="checkbox" value="@item.Value" id="GenreId" name="GenreId" />@item.Text
                        }

                    </div>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
                <a class="btn btn-primary" asp-action="Index">Back to List</a>
            </div>

        </form>
    </div>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

But what I want to achieve is to store the foreign key values 'MovieId' and 'GenreId' to the 'MovieGenre' table when creating a new movie.

你真的不需要 MovieId 因为它是在调用 SaveChanges{Async} 时自动生成的,因此只有在调用之后才可用。

但是您有显式连接实体 MovieGenre,同时具有 navigation 和显式 FK 属性,因此您可以使用导航属性 指定新的 Movie 并让 EF Core 为您处理实际的 FK。对于其他 FK,您只需分配 existing Genre 键 (Id)。

在这种情况下,有两种方法可以添加新的 MovieGenre 记录。两者都需要创建 MovieGenre 实例并填写导航和 FK 属性:

var movieGenre = new MovieGenre
{
    Movie = movies,
    GenreId = genreId // ?? - see below
};

只是不确定 GenreId 来自这里

Create([Bind("MovieId,Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType, GenreId")] Movie movies

因为 Movie class 没有 GenreId 属性,所以你必须弄清楚(最终重新访问 DTO/ViewModel 用于与控制器通信)。

一旦你有了它,你要么将它添加到 Movie.MovieGenre 集合:

movies.MovieGenre = new List<MovieGenre> { movieGenre };
_context.Add(movies);

或者直接将其添加到数据库上下文(设置)中:

_context.Add(movies);
_context.Add(movieGenre);

对于这种特殊情况,显式连接实体很有帮助。如果您改为使用 EF Core 5.0+ 隐式连接实体,则实现该操作将需要不同的方法。

我找到了正确的代码。如果有人需要这里的解决方案,我的 GitHub 带有完整代码的 repo。

https://github.com/Iam-Goku/ProjectMovie.git