Отправка формы ASP.NET возвращает нулевое значение для параметров модели при отправке

Я создаю задание на C# и столкнулся с проблемой. У меня 2 модели students и course. Мне нужно было место, где можно было бы показать всех студентов, связанных с курсом, и возможность добавить нового студента. Поэтому я создал новую модель представления CourseViewModel:

using ProblemAssignment2.Models;

namespace ProblemAssignment2.ViewModels
{
    public class CourseViewModel
    {
        public Course Course { get; set; }
        public Student NewStudent { get; set; }
    }
}

У меня есть страница Manage.cshtml в папке views/Course.

@model ProblemAssignment2.ViewModels.CourseViewModel

<h1>Manage Course</h1>

<div>
    <h4>@Model.Course.Title</h4>
    <table class = "table">
        <thead>
            <tr>
                <th>Last Name</th>
                <th>First Name</th>
                <th>Email</th>
                <th>Status</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var student in Model.Course.Students)
            {
                <tr>
                    <td>@student.LastName</td>
                    <td>@student.FirstName</td>
                    <td>@student.Email</td>
                    <td>@student.EnrollmentStatus</td>
                </tr>
            }
        </tbody>
    </table>
</div>

<h2>Create Student</h2>
<form asp-action = "createStudent" method = "post">
    <div class = "form-group">
        <label asp-for = "NewStudent.LastName" class = "control-label">Last Name</label>
        <input asp-for = "NewStudent.LastName" name = "Last" class = "form-control" />
        <span asp-validation-for = "NewStudent.LastName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "NewStudent.FirstName" class = "control-label">First Name</label>
        <input asp-for = "NewStudent.FirstName" name = "First" class = "form-control" />
        <span asp-validation-for = "NewStudent.FirstName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "NewStudent.Email" class = "control-label">Email</label>
        <input asp-for = "NewStudent.Email" name = "Email" class = "form-control" />
        <span asp-validation-for = "NewStudent.Email" class = "text-danger"></span>
    </div>
    <button type = "submit" class = "btn btn-primary">Create Student</button>
</form>

<h2>Send Confirmation Message</h2>
<form asp-action = "SendConfirmationMessage" method = "post">
    <input type = "hidden" name = "courseId" asp-for = "@Model.Course.CourseID" />
    <button type = "submit" class = "btn btn-secondary">Send Confirmation Message</button>
</form>

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

В CourseController я добавил 2 метода для отображения представления и создания ученика:

public async Task<IActionResult> Manage(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
                               .Include(c => c.Students)
                               .FirstOrDefaultAsync(m => m.CourseID == id);

    if (course == null)
    {
        return NotFound();
    }

    var viewModel = new CourseViewModel
            {
                Course = course,
                NewStudent = new Student()
            };

    return View(viewModel);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> createStudent(int id, CourseViewModel viewModel)
{
    Debug.WriteLine(viewModel.Course.CourseID);
    viewModel.NewStudent.EnrollmentStatus = Student.Status.InvitationSent;

    if (ModelState.IsValid)
    {
        var course = await _context.Courses.FindAsync(id);

        if (course == null)
        {
            return NotFound();
        }

        viewModel.NewStudent.CourseID = id;
        viewModel.NewStudent.EnrollmentStatus = Student.Status.InvitationSent; // Default status

        _context.Students.Add(viewModel.NewStudent);

        await _context.SaveChangesAsync();

        return RedirectToAction(nameof(Manage), new { id = viewModel.Course.CourseID });
    }

    // If ModelState is invalid, return the Manage view with the current viewModel
    return View(nameof(Manage), id);
}

Я могу просматривать всех студентов, связанных с курсом. Но когда я добавляю некоторые данные в форму и пытаюсь отправить их, я получаю нулевые исключения.

Я добавил точки останова на createstudent и обнаружил, что значение id равно 0, а viewModel.Course и viewModel.NewStudent оба равны нулю. В чем может быть причина того, что при отправке формы оба этих значения становятся равными нулю?

Я проверил функцию управления. Я устанавливаю значение курса, а также инициализирую newStudent для объекта.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Проблема 1. Ваше действие API ожидает получения тела запроса с вложенным объектом NewStudent, но вы отправляете значение, не вложив его во вложенный объект NewStudent. Например, элемент <input> с name = "Last".

Когда вы работаете с атрибутом name, вы должны убедиться, что имя поля соответствует свойству в теле запроса.

Например:

<input asp-for = "NewStudent.LastName" name = "NewStudent.LastName" class = "form-control" />
<input asp-for = "NewStudent.FirstName" name = "NewStudent.FirstName" class = "form-control" />
<input asp-for = "NewStudent.Email" name = "NewStudent.Email" class = "form-control" />

Или вы можете работать без атрибута name, поскольку помощник тега asp-for поможет вам сгенерировать атрибут name (если только вам не нужно настроить поле (путь) в соответствии с телом запроса в действии API).

<input asp-for = "NewStudent.LastName" class = "form-control" />
<input asp-for = "NewStudent.FirstName" class = "form-control" />
<input asp-for = "NewStudent.Email" class = "form-control" />

Для объекта Course вы должны отправить значение с помощью <input type = "hidden" />.

<input type = "hidden" asp-for = "Course.CourseID" />
<input type = "hidden" asp-for = "Course.Title" />

Проблема 2. Ваше действие API ожидает параметр id, но вы его не отправляете.

Последний элемент <form> должен быть таким:

<form asp-action = "createStudent" method = "post" asp-route-id = "@Model.Course.CourseID">

    <input type = "hidden" asp-for = "Course.CourseID" />
    <input type = "hidden" asp-for = "Course.Title" />

    <div class = "form-group">
        <label asp-for = "NewStudent.LastName" class = "control-label">Last Name</label>
        <input asp-for = "NewStudent.LastName" name = "NewStudent.LastName" class = "form-control" />
        <span asp-validation-for = "NewStudent.LastName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "NewStudent.FirstName" class = "control-label">First Name</label>
        <input asp-for = "NewStudent.FirstName" name = "NewStudent.FirstName" class = "form-control" />
        <span asp-validation-for = "NewStudent.FirstName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "NewStudent.Email" class = "control-label">Email</label>
        <input asp-for = "NewStudent.Email" name = "NewStudent.Email" class = "form-control" />
        <span asp-validation-for = "NewStudent.Email" class = "text-danger"></span>
    </div>
    <button type = "submit" class = "btn btn-primary">Create Student</button>
</form>

Вот почему ваш метод post возвращает ноль

Определение метода действия после публикации определено неправильно. В соответствии с потоком ASP.NET MVC или даже ядром формата формы вы прикрепляете модель представления к форме, поэтому в сигнатуре метода post вы не можете добавлять дополнительные параметры, если вы явно не добавите эту информацию в URL-адрес, как уже упоминалось в эта ветка. Поскольку при отправке формы вы не передаете какой-либо идентификатор информации или заголовок, связанный с курсом, ваш объект курса наверняка будет пустым или нулевым. Итак, чтобы сопоставить необходимую информацию об объекте курса, просто добавьте скрытые входные значения, чтобы отправить идентификатор и название курса в метод действия публикации.

Добавьте строку ниже в вашу форму

...


<input type = "hidden" asp-for = "Course.CourseID" />
<input type = "hidden" asp-for = "Course.Title" />

...

Измените определение метода действия публикации, как показано ниже.

...

public async Task<IActionResult> createStudent(CourseViewModel viewModel)

... 

Это решит вашу проблему.

Ответ принят как подходящий

Спасибо всем за вашу помощь. Я начал работать над решением, предоставленным @yong-shun, и внес еще несколько изменений в форму.

<form asp-action = "createStudent" method = "post" asp-route-id = "@Model.Course.CourseID">
    <div class = "form-group">
        <label asp-for = "@Model.NewStudent.LastName" class = "control-label">Last Name</label>
        <input asp-for = "@Model.NewStudent.LastName" name = "NewStudent.LastName" class = "form-control" />
        <span asp-validation-for = "NewStudent.LastName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "@Model.NewStudent.FirstName" class = "control-label">First Name</label>
        <input asp-for = "@Model.NewStudent.FirstName" name = "NewStudent.FirstName" class = "form-control" />
        <span asp-validation-for = "NewStudent.FirstName" class = "text-danger"></span>
    </div>
    <div class = "form-group">
        <label asp-for = "@Model.NewStudent.Email" class = "control-label">Email</label>
        <input asp-for = "@Model.NewStudent.Email" name = "NewStudent.Email" class = "form-control" />
        <span asp-validation-for = "NewStudent.Email" class = "text-danger"></span>
    </div>
    <button type = "submit" class = "btn btn-primary">Create Student</button>
</form>

Я изменил asp-for="NewStudent.Email" на asp-for="@Model.NewStudent.Email". ТАК добавление @Model во все поля сделало отправленную форму переданной со значениями модели.

Другие вопросы по теме