0

I am trying to display a dropdownlist for project status, that also populates with the current value in the database. The problem is that I keep getting a null reference exception on the UpdateProject action (when the button is clicked).

The error in VS2022 is

Microsoft.AspNetCore.Mvc.Razor.RazorPage.Model.get returned null.

The view model is

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
using WebServices.DomainModels.Projects;

namespace WebServices.ViewModels.Projects
{
    public class vmUpdateProject
    {
        public Project Projects { get; set; }
        public IEnumerable<SelectListItem> Priorities { get; set; }

        public string SelectedPriority { get; set; }
        public IEnumerable<SelectListItem> Statuses { get; set; }

        public string SelectedStatus { get; set; }

        public IEnumerable<SelectListItem> Urgency { get; set; }
        public string SelectedUrgency { get; set; }


    }
}

and the view segment is


    <form method="post" asp-action="UpdateProject" class="container-fluid ps-5">
        <input asp-for="Projects.ProjectId" />
        <div class="container-fluid p-3">
            <div class="row">
                <div class="col-lg-8">
                    <h1 class="text-primary">Update Project Details</h1>
                </div>
                <div class="col-lg-2">
                    <input type="submit" class="btn btn-info w-100" value="Update Project" />
                    @*<a asp-action="UpdateProject" class="btn btn-info w-100">Update Project</a>*@
                </div>
                <div class="col-lg-2">
                    <a asp-action="Dashboard" asp-controller="Project" class="btn btn-danger w-100">Project Dashboard</a>
                    <p class="small">Returns you to the dashboard. You will lose any changes</p>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-lg-12 float-right">
                <div class="col-lg-6">
                    Project Status
                </div>
                <div class="col-lg-6">
                    @Html.DropDownListFor(m => Model.Projects.ProjectStatus, new SelectList(Model.Statuses,"Value","Text",Model.Projects.ProjectStatus))
                </div>
            </div>
        </div>
    </form>

The view displays and fills all the data in fine. The DropDownList is populated with the list items, but when I click Update Project, it gets a null reference exception on the line @Html.DropDownListFor section and I have no idea what I am missing that is causing this.

The controller GET action is

public IActionResult UpdateProject(int? id)
        {
            if (id == null || id == 0)
            {
                return NotFound();
            }
            var vmProjectDetails = new vmUpdateProject();
            vmProjectDetails.Projects = ReturnProjectDetails(id, dbWebServicesString);
            vmProjectDetails.Priorities = GetSelectListItems("ProjectPriority", dbWebServicesString);
            vmProjectDetails.Statuses = GetSelectListItems("ProjectStatus", dbWebServicesString);
            vmProjectDetails.Urgency = GetSelectListItems("ProjectUrgency", dbWebServicesString);
            //var obj = _db.Projects.Find(id);
            //if(obj == null)
            //{
            //    return NotFound();
            //}
            return View(vmProjectDetails);
        }

The post action is:


        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult UpdateProject(vmUpdateProject obj)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _db.Projects.Update(obj.Projects);
                    _db.SaveChanges();
                    return RedirectToAction("Dashboard", "Project");

                }
                else
                {
                    //TODO: Put in error handling for ModelState.IsValid
                    return View();
                }
            }
            catch (SqlTypeException ex)
            {
                return null;
            }
        }

Regards, Darren

Darren M
  • 65
  • 1
  • 8
  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – jazb Mar 29 '22 at 02:08
  • Thanks for the link, but I understand what the NullReference means, I don't know why it is null, as everything else in the View is fine and the SelectList is populated in the controller ok, which is reflecting in the list of items that appear in the dropdownlist. – Darren M Mar 29 '22 at 02:14
  • 1
    @DarrenM Maybe stick a breakpoint inside `IActionResult UpdateProject` and confirm that this handler is the one being called? I'm wondering if you accidentally have a different handler creating your view, which isn't passing the model. *Edit to clarify* - check that this handler is the one that is handling your *postback* (or whatever it's called in MVC?). Presumably it is the handler creating the page initially, since that works – brads3290 Mar 29 '22 at 02:54
  • I try your code, the post method can just get the value of `ProjectId` and `ProjectStatus` in `vmUpdateProject`, But the value of the property of type `IEnumerable` is null, So from the error message, I guess you just do something with the mode directly in post method. Can you provide your post method? – Xinran Shen Mar 29 '22 at 07:36
  • I've added the post method to the original post. But when I put a breakpoint on this, it doesn't reach this action, it errors out on the @html.dropdownlistfor line in the view. – Darren M Mar 29 '22 at 22:55
  • Hi @DarrenM, Try Rahul Sharma's method, And changed it like `Priorities = new IList(); ....` in Constructor. – Xinran Shen Mar 30 '22 at 00:46
  • @XinranShen I tried that and now I get the error [Cannot create an instance of the abstract type or interface IList] instead of for IEnumerable – Darren M Mar 30 '22 at 02:52

1 Answers1

1

You firstly need to change the type of the SelectList from IEnumerable<SelectListItem> to List<SelectListItem> and you also need to instantiate a new List<SelectListItem> in your Model constructor. Change your Model to:

public class vmUpdateProject
{

    public vmUpdateProject()
    {
        Priorities = new List<SelectListItem>();
        Statuses = new List<SelectListItem>();
        Urgency = new List<SelectListItem>();
    }
    
    public Project Projects { get; set; }
    public List<SelectListItem> Priorities { get; set; }

    public string SelectedPriority { get; set; }
    public List<SelectListItem> Statuses { get; set; }

    public string SelectedStatus { get; set; }

    public List<SelectListItem> Urgency { get; set; }
    public string SelectedUrgency { get; set; }

}

You can not instantiate an instance of an interface, and you have declared your SelectListItem as IEnumerable which is an interface. This rule also applies to abstract classes.

Rahul Sharma
  • 7,768
  • 2
  • 28
  • 54
  • Thank Rahul. Do I need to change the view to reference this? When I change the model to the above I get the error Cannot create an instance of the abstract class or interface 'IEnumerable' – Darren M Mar 29 '22 at 22:55
  • @DarrenM Change your type from `IEnumerable` to a `List` and try again. Actually, you can not instantiate an instance of an interface, and you have declared your `SelectListItem` as `IEnumerable` which is an interface. – Rahul Sharma Mar 30 '22 at 06:42