Как добавить несколько экземпляров класса T в свойство List of T при создании экземпляра другого класса ASP.NET Core

Имеется класс Skill и класс Course

public class Course
    {
        public int Id { get; set; }

        public string? Name { get; set; }

        public string? Description { get; set; }

        public List<Skill>? CourseSkills { get; set; }

        [Required(ErrorMessage = "You must be authorized!")]
        public string CreatorUserName { get; set; }
    }
public class Skill
    {
        public string Name { get; set; }

        public string? Description { get; set; }

        public List<Course>? Courses { get; set; }

        public int Id { get; set; }
    }
public class CourseViewModel
    {
        public List<SelectListItem>? SkillSelectList { get; set; }

        public int SelectedSkillId { get; set; }

        public int Id { get; set; }

        public string Name { get; set; }

        public string? Description { get; set; }

        [Required(ErrorMessage = "You must be authorized!")]
        public string CreatorUserName { get; set; }
    }

При создании экземпляра класса Course через стандартный ASP.NET CRUD интерфейс необходимо как-то добавлять несколько уже существующих экземпляров класса Skill из списка внутри select-a


Как я себе это представляю:
(input) [_ введите Имя для Course _]
(select) [_ выберите Skill _] (button) [_ добавить Skill в Course _]
(button) [_ создать Course _]


При нажатии на (button) [_ добавить Skill в Course _] ниже текущего select-a появляется еще один select и button, где можно было бы так же выбрать еще Skill
И при нажатии (button) [_ создать Course _] в метод Create контроллера CourseController передавался бы List_of_Skills или Array_of_Skills (+ прочие свойства, типа Name и тд)

Либо просто в одном select-e выбирать несколько и передавать их в контроллер в виде List или Array


Пока что разобрался только с добавлением одного Skill в Course :

@model Prog.Models.ViewModels.CourseViewModel
@{
    ViewData["Title"] = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Create course</h1>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group mb-3">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="mb-3 form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Description" class="control-label"></label>
                <input asp-for="Description" class="mb-3 form-control" />
                <span asp-validation-for="Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="CreatorUserName" class="control-label"></label>
                <input asp-for="CreatorUserName" class="mb-3 form-control" value="@User.Identity?.Name" readonly="true"/>
            </div>  
                <div class="form-group mb-3">
                    <select class="form-select" asp-items="Model.SkillSelectList" asp-for="SelectedSkillId">
                        <option value="0">-- Please Select a Skill --</option>
                    </select>
                </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-outline-primary" />
            </div>
        </form>
    </div>
</div>

Идеи, варианты?


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

Автор решения: Andrei Brizhak

Модель Course

public class Course
{
    public int Id { get; set; }

    public string? Name { get; set; }

    public string? Description { get; set; }
    
    public int SkillId { get; set; } 

    [ForeignKey("SkillId")]
    public virtual Skill? Skill { get; set; }

    [Required(ErrorMessage = "You must be authorized!")]
    public string CreatorUserName { get; set; }
}

Фрагмент кода метода Create для Контроллера Course (для архитектуры MVC)

        public IActionResult Create()
    {    
         // извлечение скиллов из базы данных и конвертация в объект специального типа
         IEnumerable<SelectListItem> SkillsDropdown = _db.Skills.Select(i=> new SelectListItem { Text=i.Name, Value=i.Id.ToString()});

        // создание ViewBag для передачи в представление
         ViewBag.SkillsDropDown= SkillsDropDown;
         Course course = new();
         return View(course)
    }

View

@model Course
<form method="post"
    .....
// блок с выбором скилов из выпадающего меню
<div>
 <select asp-for="SkillId" asp-items="@ViewBag.SkillsDropDown"
</div>

//передача данных в метод пост из полей ввода
 <div>
   <input type="submit" value="Create"
</div>

Также в контроллере нужно будет создать метод Create типа POST, для записи в базу данных нового Course.
ViewBag это костыль, и правильнее создавать ViewModel для работы с данными о Course. Подробный пример решения, для вашего случая в курсе по ссылке.

→ Ссылка
Автор решения: lancwork

Если правильно понял вы хотите передать в модель CourseViewModel несколько Skill. Тогда в классе CourseViewModel заменим свойство

public int SkillId { get; set; } 

на свойство

public List<int> SkillIds { get; set; }

так как нам нужен список, а не один элемент.


Далее, чтобы с представления получать список значений, к тегу select добавьте атрибут multiple. В общем должно получиться так:

<select multiple class="form-select" asp-items="Model.SkillSelectList" asp-for="SkillIds">
</select>

на самом представлении это будет выглядеть так: Стандартный мультиселект в представлении

Да, стандартный мультиселект выглядит не очень, к этому вернёмся позже. Теперь зажимаем CTRL и выбираем нужные элементы (в стандартном только так). Далее нажав Create в контролере мы увидим выбранные элементы, для примера выбрал 1 и 3: Полученные id-шки с представления


Теперь изменим стандартный мультиселект. Если погуглить есть много вариантов/стилей для мультиселекта, мне понравился отсюда https://github.com/snapappointments/bootstrap-select. И в итоге будет выглядеть так, и через CTRL уже выбирать не надо

3

По ссылке описана установка, но если коротко:

  • через nuget установить bootstrap-select
  • в файле _Layout:
    • в head добавить <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap-select.min.css">
    • после jquery.js и bootstrap.js добавить <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-select.min.js"></script>
  • к селекту добавить класс selectpicker благодоря этому преобразование в селект что на последнем скрине происходит автоматически
→ Ссылка