Search code examples
c#asp.netasp.net-mvc

ASP.NET form submission returning null value for model parameters when submitting


I am building a C# assignment and facing an issue. I have 2 models students and course. I needed a place to show all the students related to a course and ability to add new student. So I created a new view model CourseViewModel:

using ProblemAssignment2.Models;

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

I have a Manage.cshtml page in views/Course folder.

@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");}
}

In CourseController, I have added 2 methods to show view and create student:

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);
}

I am able to view all the students related to course. But when I add some data to form and try to submit it, I get null exceptions.

I added breakpoints on createstudent and found out that id value is 0 and viewModel.Course and viewModel.NewStudent both are null. What can be reason that on submit of form both these values are getting set to null?

I have checked in manage function I am setting course value and also initializing newStudent to a object.


Solution

  • Thanks everyone for your help. I started working on the solution provided by @yong-shun and made a few more changes to the form.

    <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>
    

    I changed asp-for="NewStudent.Email" to asp-for="@Model.NewStudent.Email". SO adding @Model in all the fields made submitted form passed with model values.