0

I'm getting the following error each Time I try to enter a new Course.


Object reference not set to an instance of an object. AspNetCore._Views_Admin_Manage_cshtml+<b__23_12>d.MoveNext() in Manage.cshtml, line 34


Here is my Controller:

using ASP_Project.Data;
using ASP_Project.Models;
using ASP_Project.Services.Interfaces;
using ASP_Project.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace ASP_Project.Controllers
{
    public class AdminController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SchoolContext _schoolContext;
        private readonly IAdminRepository _adminRepos;
        private readonly ITeacherRepository _teacherRepository;

        public AdminController(UserManager<ApplicationUser> userManager,
            SchoolContext schoolContext,
            IAdminRepository adminRepos,
            ITeacherRepository teacherRepository
            )
        {
            _userManager = userManager;
            _schoolContext = schoolContext;
            _adminRepos = adminRepos;
            _teacherRepository = teacherRepository;
        }

        [HttpGet]
        [Authorize(Roles = "Admin")]
        public async Task<IActionResult> Index()
        {
            ClaimsPrincipal currentUser = User;
            var user = await _userManager.GetUserAsync(currentUser);
            var admin = _adminRepos.GetAdminByUser(user);

            return View(new AdminViewModel()
            {
                FirstName = admin.FirstName,
                LastName = admin.LastName,
                MiddleName = admin.MiddleName
            });
        }

        [HttpGet]
        public IActionResult Manage()
        {
            IEnumerable<string> teachers = _teacherRepository.TeacherNames();
            return View(new CourseViewModel()
            {
                Teachers = teachers
            });
        }

        [HttpPost]
        public async Task<IActionResult> Manage(CourseViewModel courseViewModel)
        {
            var teacher = _schoolContext.Teacher.Single(t => t.FirstName == courseViewModel.TeacherName);
            Course course = new Course()
            {
                CodeID = courseViewModel.CodeID,
                Name = courseViewModel.Name,
                NumOfCredits = courseViewModel.NumOfCredits,
                TeacherID = teacher.TeacherID
            };
            await _schoolContext.Course.AddAsync(course);
            if (await _schoolContext.SaveChangesAsync() == 0)
                return RedirectToAction("Index", "Admin");
            return View(courseViewModel);
          }
       }
  }

Here is my View:

@model ASP_Project.ViewModels.CourseViewModel
@{
    ViewData["Title"] = "Manage";
}

<h2>Manage</h2>

<div class="row">
    <div class="col-md-4">
        <form asp-controller="Admin" asp-action="Manage" method="post" class="form-horizontal" role="form">
            <h4>Create a new Course.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="CodeID"></label>
                <input asp-for="CodeID" class="form-control" />
                <span asp-validation-for="CodeID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Name"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="NumOfCredits"></label>
                <input asp-for="NumOfCredits" class="form-control" />
                <span asp-validation-for="NumOfCredits" class="text-danger"></span>
            </div>
            <div>
                <label asp-for="TeacherName" class="col-md-2 control-label"></label>
                <div class="col-md-10">
                    <select asp-for="TeacherName" class="form-control" required>
                        <option value="" disabled selected>Select Teacher</option>
                        @foreach (var teach in Model.Teachers)
                        {
                            <option value="@teach"> @teach </option>
                        }
                    </select>
                    <span asp-validation-for="TeacherName" class="text-danger"></span>
                </div>
            </div>

            <button type="submit" class="btn btn-default">Add</button>
        </form>
    </div>
</div>
@section Scripts {
    @await Html.PartialAsync("_ValidationScriptsPartial")
}

My CourseViewModel:

using ASP_Project.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace ASP_Project.ViewModels
{
    public class CourseViewModel
    {
        [Required]
        public string CodeID { get; set; }
        [Required]
        public int NumOfCredits { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public string TeacherName { get; set; }
        public IEnumerable<string> Teachers { get; set; } 
    }
}

And Finally the Function used to retrieve the names of the teachers:

public IEnumerable<string> TeacherNames() => _schoolContext.Teacher.Select(t => t.FirstName);

What I understood from the exception is that there is either a part of the foreach that needs an await or that one of the Objects is not being defined.

Take note that the operation is doing its job successfully nonetheless and the data is being added to the database, its just that this strange exception keeps showing up. Edit: Even though @NoCodeFound answer Pointed out that I should debug (and that's what I did to find the answer) yet I was planning on doing that anyway, and I happened to discover the real cause anyway.

Ali Haroon
  • 83
  • 1
  • 4
  • 12
  • That one seems helpful but my problem seems a bit strange since I can't really relate it to a specific cause. – Ali Haroon May 06 '18 at 23:05
  • 1
    It says the error is at line 34 of your view, which is iterating through `Model.Teachers` which is most likely null. I would make sure you are passing the correct object into `View(courseViewModel)`. – cjkeilig May 07 '18 at 02:40
  • But the select is showing the names I need as well as updating the database after pressing submit. The problem is that it shows the exception even though nothing is showing null. – Ali Haroon May 07 '18 at 08:51

1 Answers1

0

Turns out I messed up when I return from the Manage action after POST, since I used:

if (await _schoolContext.SaveChangesAsync() == 0)
   return RedirectToAction("Index", "Admin");
return View(courseViewModel);

which was making me go through the courseViewModel again rather than being redirected to the page I needed. So the fix would simply be:

if (await _schoolContext.SaveChangesAsync() == 0)
                return View(courseViewModel);
return RedirectToAction("Index", "Admin");
Ali Haroon
  • 83
  • 1
  • 4
  • 12
  • 1
    You can still get that exception, because when you execute `return View(courseViewModel);` - the value of `Model.Teachers` is `null` (you have not re-populated it as you did in the GET method). –  May 07 '18 at 09:33
  • Yeah You're right, Thanks for the remark. – Ali Haroon May 07 '18 at 11:39