Не могу получить модели на бэк из view по связи many-to many в приложении MVC

Помогите, пожалуйста, разобраться с проблемой. Делаю проект Movie. БД подключал через CodeFirst подход. Есть модели со связями many-to-many (к примеру Actor и Movie). После реализации контроллера с методом create, при создании нового фильма, во вьюшке не могу получить данные, т.е. у меня отображаются доступные актеры, но я не могу их выбрать, фильм создается с пустыми данными. Но если прописать связи напрямую в БД (через SQLManager, что ActorId = 1 принадлежит MovieId = 3), то все срабатывает. выбираю актера (но нужно выбирать несколько)

вот такой результат

вот как нужно

вот каким кодом пытаюсь это получить модели

    public class Actor
{
    public int ActorId { get; set; }

    [Required(ErrorMessage = "Full Name is required")]
    [StringLength(50, MinimumLength = 3, ErrorMessage = "Last name cannot be longer than 50 characters and less 3.")]
    [Column("FullName")]
    public string FullName { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
    public DateTime DayOfBirth { get; set; }

    [Required(ErrorMessage = "Image is required")]
    public string ImageUrl { get; set; }

    public string Biografy { get; set; }

    //movies
    public virtual ICollection<Movie> Movies { get; set; }
   }

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

    [Required(ErrorMessage = "Title is required")]
    [Display(Name = "Title"), StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Required(ErrorMessage = "Image is required")]
    public string ImageUrl { get; set; }

    [StringLength(500, MinimumLength = 3, ErrorMessage = "Descriptipn cannot be longer than 500 characters and less 3.")]
    public string Description { get; set; }

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    //Genre
    public Genre Genre { get; set; }

    //Rating
    public Rating Rating { get; set; }

    //actors
    public virtual ICollection<Actor> Actors { get; set; }

    //producers
    public virtual ICollection<Producer> Producers { get; set; } }

Контроллер с методом Create

 public class MoviesController : Controller
{
    private readonly IMoviesRepository _service;

    private readonly IProducerRepository _producer;

    private readonly IToastNotification _toastNotification;


    public MoviesController(IMoviesRepository service, IToastNotification toastNotification, IProducerRepository producer)
    {
        _service = service;

        _toastNotification = toastNotification;

        _producer = producer;
    }

    // GET: Movies/Create
    public async Task<IActionResult> Create()
    {
        var movieDropdownsData = await _service.GetMovieDropdownsValues();

        ViewBag.Producers = new MultiSelectList(movieDropdownsData.SelectedProducers, "ProducerId", "FullName");
        ViewBag.Actors = new SelectList(movieDropdownsData.SelectedActors, "ActorId", "FullName");

        return View();
    }

    // POST: Movies/Create
    // To protect from overposting attacks, enable the specific properties you want to bind to.
    // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("MovieId,Title,ImageUrl,Description,ReleaseDate,Genre,Ratin,ActorsId,ProducersId")] Movie movie)
    {
        if (ModelState.IsValid)
        {
            await _service.AddAsync(movie);

            _toastNotification.AddSuccessToastMessage("Movie created");

            return RedirectToAction(nameof(Index));
        }
        var movieDropdownsData = await _service.GetMovieDropdownsValues();

        ViewBag.Producers = new MultiSelectList(movieDropdownsData.SelectedProducers, "ProducerId", "FullName");
        ViewBag.Actors = new SelectList(movieDropdownsData.SelectedActors, "ActorId", "FullName");

        return View(movie);
    } }

вот вьюшка на создание фильма

@model WebAppMovie.Models.Movie

@using WebAppMovie.Data.Enums
@using WebAppMovie.Data


@{
  ViewData["Title"] = "Create a new movie";
 }


 <br />
 <h1 class="mt-5">Create Movies</h1>
 <br />



 <div class="row">
  <div class="col-md-8 offset-2">
    <div class="row flex-nowrap">
        <div class="form-group text-center">
            <img id="ImagePreview" style="max-width: 350px" />
        </div>
        <div class="col-md-8 offset-2">
            <form asp-action="Create">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="row">
                    <div class="col-md-6">
                        <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="ReleaseDate" class="control-label"></label>
                            <input asp-for="ReleaseDate" type="date" class="form-control" />
                            <span asp-validation-for="ReleaseDate" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="ImageUrl" class="control-label"></label>
                            <input asp-for="ImageUrl" class="form-control" />
                            <span asp-validation-for="ImageUrl" class="text-danger"></span>
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="form-group">
                            <label asp-for="Genre" class="control-label"></label>
                            <select asp-for="Genre" class="form-control" asp- 
                      items="Html.GetEnumSelectList<Genre>()"></select>
                            <span asp-validation-for="Genre" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Rating" class="control-label"></label>
                            <select asp-for="Rating" class="form-control" asp- 
                     items="Html.GetEnumSelectList<Rating>()"></select>
                            <span asp-validation-for="Rating" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="@Model.Producers" class="control-label"></label>
                            <select asp-for="@Model.Producers" class="form-control" asp- 
                   items="ViewBag.Producers" multiple></select>
                            @*@Html.DropDownListFor(m => m.Producers, 
                      (MultiSelectList)ViewBag.Producers)*@
                            @*@Html.ListBoxFor(m => m.Producers, 
                  (MultiSelectList)ViewBag.Producers)*@
                            <span asp-validation-for="@Model.Producers" class="text-danger"> 
                    </span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Actors" class="control-label"></label>
                            <select asp-for="Actors" class="form-control" asp- 
                         items="ViewBag.Actors"></select>
                            <span asp-validation-for="Actors" class="text-danger"></span>
                        </div>
                    </div>
                    <div class="col-md-12">
                        <div class="form-group">
                            <label asp-for="Description" class="control-label"></label>
                            <textarea asp-for="Description" class="form-control"></textarea>
                            <span asp-validation-for="Description" class="text-danger"></span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <input type="submit" value="Create" class="btn btn-outline-success float-right" />
                    <a class="btn btn-outline-secondary" asp-action="Index">Back to List</a>
                </div>
            </form>
        </div>
       </div>
      </div>
     </div>

   @section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script>
     $("#ImageUrl").on("change", function () {
        var output = document.getElementById('ImagePreview');
        output.src = $(this).val();
     })
    </script>
     }

ну и как пытаюсь связать Movie с Actor

public async Task<NewMovieDropdown> GetMovieDropdownsValues()
    {
        var response = new NewMovieDropdown()
        {
            SelectedActors = await _context.Actors.OrderBy(n => n.FullName).ToListAsync(),
            SelectedProducers = await _context.Producers.OrderBy(n => n.FullName).ToListAsync()
        };

        return response;
    }

    public class NewMovieDropdown
    {
        public NewMovieDropdown()
        {
            SelectedProducers = new List<Producer>();

            SelectedActors = new List<Actor>();
        }

        public List<Producer> SelectedProducers { get; set; }
        public List<Actor> SelectedActors { get; set; }
    }

создаю таблицы в БД

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }
    public DbSet<Actor> Actors { get; set; }
    public DbSet<Movie> Movies { get; set; }
    public DbSet<Producer> Producers { get; set; }
    //public DbSet<Score> Scores { get; set; }
    public DbSet<Person> Persons { get; set; }
}

Буду благодарен за любую помощь. Спасибо.


Ответы (1 шт):

Автор решения: Peterblr

В общем для начала нужно было в классе class ApplicationDbContext добавить метод с указанием ключей

        modelBuilder.Entity<Movie>()
            .HasMany(p => p.Actors)
            .WithMany(m => m.Movies)
            .UsingEntity<ActorMovies>(
                j => j
                    .HasOne(pm => pm.Actor)
                    .WithMany()
                    .HasForeignKey(p => p.ActorId),
                j => j
                    .HasOne(pm => pm.Movie)
                    .WithMany()
                    .HasForeignKey(m => m.MovieId)
            );

        base.OnModelCreating(modelBuilder);

потом создать класс связующий

public class ActorMovies
{
    public int ActorId { get; set; }
    public Actor Actor { get; set; }


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

далее класс с новым фильмом

public class NewMovieViewModel
{
    public int NewMovieId { get; set; }

    [Required(ErrorMessage = "Title is required")]
    [Display(Name = "Title"), StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }


    [Required(ErrorMessage = "Image is required")]
    public string ImageUrl { get; set; }


    [StringLength(1500, MinimumLength = 3, ErrorMessage = "Descriptipn cannot be longer than 1500 characters and less 3.")]
    public string Description { get; set; }


    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }


    //Genre
    public Genre Genre { get; set; }


    //Rating
    public Rating Rating { get; set; }

    //Grade
    //public List<double> Grade { get; set; }


    //actors
    public List<int> ActorsMovieId { get; set; }


    //producers
    public List<int> ProducersMovieId { get; set; }


    //comments        
    public List<Comment> CommentsMovie { get; set; }
}

нужно описать новый метод, который достает актеров по id

 public async Task AddNewMovieAsync(NewMovieViewModel data)
    {
        var newMovie = new Movie()
        {
            Title = data.Title,
            ImageUrl = data.ImageUrl,
            Description = data.Description,
            ReleaseDate = data.ReleaseDate,
            Genre = data.Genre,
            Rating = data.Rating,
            Comments = data.CommentsMovie,
        };

        await _context.Movies.AddAsync(newMovie);

        await _context.SaveChangesAsync();

        foreach (var producer in data.ProducersMovieId)
        {
            var newProducerMovies = new ProducerMovies()
            {
                MovieId = newMovie.MovieId,
                ProducerId = producer
            };

            await _context.AddAsync(newProducerMovies);
        }

        await _context.SaveChangesAsync();

        foreach (var actors in data.ActorsMovieId)
        {
            var newActorMovies = new ActorMovies()
            {
                MovieId = newMovie.MovieId,
                ActorId = actors
            };

            await _context.AddAsync(newActorMovies);
        }

        await _context.SaveChangesAsync();
    }

ну и во вьюшке добавить возможность выбирать по id

             <div class="form-group">
             <label asp-for="ProducersMovieId" class="control-label"></label>
             <select asp-for="ProducersMovieId" class="form-control" asp-items="ViewBag.Producers" multiple></select>
             <span asp-validation-for="ProducersMovieId" class="text-danger"> 
               </span>
             </div>

И обязательно прописать в контроллере с методом Create выбранный параметр ProducersMovieId в Bind[]

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("MovieId,Title,ImageUrl,Description,ReleaseDate,Genre,Rating,ProducersMovieId,ActorsMovieId,CommentId")] NewMovieViewModel movie)
    {
        if (ModelState.IsValid)
        {
            await _service.AddNewMovieAsync(movie);

            _toastNotification.AddSuccessToastMessage("Movie created");

            return RedirectToAction(nameof(Index));
        }
        var movieDropdownsData = await _service.GetMovieDropdownsValues();

        ViewBag.Producers = new SelectList(movieDropdownsData.SelectedProducers, "ProducerId", "FullName");
        ViewBag.Actors = new SelectList(movieDropdownsData.SelectedActors, "ActorId", "FullName");

        return View(movie);
    }

И только после этого все получилось.

→ Ссылка