Я создаю задание на 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 для объекта.
Проблема 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 во все поля сделало отправленную форму переданной со значениями модели.