1

I'm sure this is something really obvious but I've spent hours (including a lot of Googling) on this and I am stuck.

I am able to create a record (along with the Fluent Validations firing where need be) but immediately upon clicking on a record's Edit button, I'm getting an error

ArgumentNullException: Value cannot be null. (Parameter 'items')

When I debug, I can see that within the Edit GET code, the return View(docTrailList) is being populated correctly.

Then, when I click on the first part of the code in the HttpPost section, I get the above noted null message. While in debug mode, if I hover over the code within the HttpPost section, I can see that it is capturing the data. This model's code is identical to other models' code and they are working fine.

What am I missing? Help most gratefully received.

using System;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;
using DocumentFormat.OpenXml.Office2010.Excel;
using FluentValidation;
using FluentValidation.AspNetCore;
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Research.Data;
using ValidatorAttribute = ServiceStack.FluentValidation.Attributes.ValidatorAttribute;

using Research.Models;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;

namespace Research.Controllers
{
    public class DocTrailListsController : Controller
    {
        private readonly ResearchContext _context;

        public DocTrailListsController(ResearchContext context)
        {
            _context = context;
        }

        // GET: DocTrailLists
        public async Task<IActionResult> Index(
            string sortOrder,
            string currentFilter,
            string searchString,
            int? pageNumber)
        {
            ViewData["CurrentSort"] = sortOrder;
            ViewData["ProjIDSortParm"] = String.IsNullOrEmpty(sortOrder) ? "Proj_desc" : "";

            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }

            ViewData["CurrentFilter"] = searchString;

            var dTrailL = from s in _context.DocTrailList
                          select s;

            if (!String.IsNullOrEmpty(searchString))
            {
                dTrailL = dTrailL.Where(s => s.ProjectID.HasValue && s.ProjectID.Value.ToString().Contains(searchString));
            }

            switch (sortOrder)
            {
                case "Proj_desc":
                    dTrailL = dTrailL.OrderByDescending(s => s.ProjectID);
                    break;

                default:
                    dTrailL = dTrailL.OrderBy(s => s.ProjectID);
                    break;
            }

            int pageSize = 100;

            return View(await PaginatedList<DocTrailList>.CreateAsync(dTrailL.AsNoTracking(), pageNumber ?? 1, pageSize));
        }

        // GET: DocTrailLists/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null || _context.DocTrailList == null)
            {
                return NotFound();
            }

            var docTrailList = await _context.DocTrailList
                .Include(d => d.PIF)
                .FirstOrDefaultAsync(m => m.DetailID == id);

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

            return View(docTrailList);
        }

        // GET: DocTrailLists/Create
        [HttpGet]
        public IActionResult Create()
        {
            ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID");

            var dtname = _context.DocType!.ToList();
            ViewBag.dtname = dtname;

            var current = _context.YesNoList!.ToList();
            ViewBag.current = current;

            return View();
        }

        public Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary GetModelState()
        {
            return ModelState;
        }

        // POST: DocTrailLists/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("DetailID,ProjectID,DocumentType,Version,Dated,DateReceived,IsThisCurrent")] DocTrailList docTrailList,
            string action,
            string returnUrl)
            //string returnUrl, 
            //Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary modelState)
        {
            DocTrailListsValidator doctraillistsvalidator = new DocTrailListsValidator(_context);
           
            ValidationResult result = doctraillistsvalidator.Validate(docTrailList);

            // When validation failed, return to Index View
            if (!ModelState.IsValid)
            {
                foreach (var failure in result.Errors)
                {
                    ModelState.AddModelError(failure.PropertyName, failure.ErrorMessage);
                }

                ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);

                var dtname = _context.DocType!.ToList();
                ViewBag.dtname = dtname;

                var current = _context.YesNoList!.ToList();
                ViewBag.current = current;

                return View(docTrailList);
            }

            // Perform DB update after validation is passed
            if (action == "SaveAndBack" && !String.IsNullOrEmpty(returnUrl))
                {
                    try
                    {
                        _context.Add(docTrailList);
                        await _context.SaveChangesAsync();


                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (!DocTrailListExists(docTrailList.DetailID))
                        {
                            return NotFound();
                        }
                        else
                        {
                            throw;
                        }
                    }

                    // Ensure all code paths return a value
                    // Redirection after model validation passed and successfully update to database
                    var redirectUrl = Url.Action("Details", "PIFs", new { id = docTrailList.ProjectID });

                    //if (action == "SaveAndBack" && !String.IsNullOrEmpty(returnUrl + 1))
                    if (string.IsNullOrEmpty(redirectUrl))
                    {
                        return NotFound(); // Handle the case where the URL could not be generated
                    }
                    return Redirect(redirectUrl);
                }
                else
                {
                    try
                    {
                        _context.Add(docTrailList);
                        await _context.SaveChangesAsync();
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (!DocTrailListExists(docTrailList.DetailID))
                        {
                            return NotFound();
                        }
                        else
                        {
                            throw;
                        }
                        //return View(sAE);

                    }
                    return RedirectToAction(nameof(Index));
                }
            }

        }

        // GET: SAEs/Edit/5
        [HttpGet]
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null || _context.DocTrailList == null)
            {
                return NotFound();
            }

            var docTrailList = await _context.DocTrailList.FindAsync(id);
            if (docTrailList == null)
            {
                return NotFound();
            }

            ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);

            var dtname = _context.DocType!.ToList();
            ViewBag.dtname = dtname;

            var current = _context.YesNoList!.ToList();
            ViewBag.current = current;

            return View(docTrailList);
        }

        // POST: SAEs/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id,[Bind("DetailID,ProjectID,DocumentType,Version,Dated,DateReceived,IsThisCurrent")] DocTrailList docTrailList,
           string action,
           string returnUrl)
        {
            if (id != docTrailList.DetailID)
            {
                return NotFound();
            }

                DocTrailListsValidator docTrailListsValidator = new DocTrailListsValidator(_context);
                ValidationResult result = docTrailListsValidator.Validate(docTrailList);

            {
                if (!ModelState.IsValid)
                {
                    foreach (var failure in result.Errors)
                    {
                        ModelState.AddModelError(failure.PropertyName, failure.ErrorMessage);
                    }
                    ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);

                    var dtname = _context.DocType!.ToList();
                    ViewBag.dtname = dtname;

                    var current = _context.YesNoList!.ToList();
                    ViewBag.current = current;

                    return View(docTrailList);
                }
                    // Perform DB update after validation is passed
                if (action == "SaveAndBack" && !String.IsNullOrEmpty(returnUrl))
                {
                    try
                    {
                        _context.Update(docTrailList);
                        await _context.SaveChangesAsync();
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (!DocTrailListExists(docTrailList.DetailID))
                        {
                            return NotFound();
                        }
                        else
                        {
                            throw;
                        }
                    }

                    // Ensure Url.Action does not return null before passing it to Redirect
                    var redirectUrl = Url.Action("Details", "PIFs", new { id = docTrailList.ProjectID });

                    if (string.IsNullOrEmpty(redirectUrl))
                    {
                        return NotFound(); // Handle the case where the URL could not be generated
                    }

                    return Redirect(redirectUrl);
                }
                else
                {
                    try
                    {
                        _context.Update(docTrailList);
                        await _context.SaveChangesAsync();
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (!DocTrailListExists(docTrailList.DetailID))
                        {
                            return NotFound();
                        }
                        else
                        {
                            throw;
                        }
                    }

                    return RedirectToAction(nameof(Index));
                }
            }
        }
  

Edit.cshtml:

@model Research.Models.DocTrailList
@using System.Web.Optimization
@using Microsoft.AspNetCore.Mvc
@using System.Web
@using Microsoft.AspNetCore.Html
@using FluentValidation.AspNetCore
@using FluentValidation
@using FluentValidation.Results;

@{
    ViewData["Title"] = "Edit";
}
@Html.ValidationSummary(true)


<h2>Edit:  Document Trail</h2>
<style>
    body {
        background-color: blanchedalmond;
    }

    .ProjID {
        height: 80px;
        margin: 20px;
        border: 5px solid;
        background-color: burlywood;
    }

    .DocType {
        height: 50px;
        margin: 20px;
        border: 5px solid;
        background-color: darkkhaki;
    }

    .Version {
        height: 80px;
        margin: 20px;
        border: 5px solid;
        background-color: lightblue;
    }

    .DReceived {
        height: 80px;
        margin: 20px;
        border: 5px solid;
        background-color: lavender;
    }

</style>
<body>
    <div class="container">
        <div class="row justify-content-start">
            <form asp-action="Edit">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <input type="hidden" asp-for="DetailID" />
                <div class="ProjID">
                    <div class="row justify-content-start">
                        <div class="col">
                            <div class="form-group">
                                <label asp-for="ProjectID" class="control-label"></label>
                                <select asp-for="ProjectID" class="form-control" asp-items="ViewBag.ProjectID"></select>
                                <span asp-validation-for="ProjectID" class="text-danger"></span>
                                 <div class="col-md-10"> 
                                   @Html.ValidationMessageFor(model => model.ProjectID, null, new { @class = "text-danger" })
                                </div> 
                            </div>
                        </div>
                        <br />
                    </div>
                </div>
                <div class="DocType">
                    <div class="row justify-content-start">
                        <div class="col">
                            <div class="form-group"> 
                                <label asp-for="DocumentType" class="control-label"></label>
                                @Html.DropDownList("DocumentType", new SelectList(ViewBag.dtnamelist,"DocumentTypeName","DocumentTypeName"),"")
                            </div>
                        </div>
                        <br />
                    </div>
                </div>
                <div class="Version">
                    <div class="row justify-content-start">
                        <div class="col">
                            <div class="form-group">
                                <label asp-for="Version" class="control-label"></label>
                                <input asp-for="Version" class="form-control" />
                                <span asp-validation-for="Version" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="col">
                            <div class="form-group">
                                <label asp-for="Dated" class="control-label"></label>
                                <input asp-for="Dated" class="form-control" />
                                <span asp-validation-for="Dated" class="text-danger"></span>
                            </div>
                        </div>
                        <br />
                    </div>
                </div>
                <div class="DReceived">
                    <div class="row justify-content-start">
                        <div class="col">
                            <div class="form-group">
                                <label asp-for="DateReceived" class="control-label"></label>
                                <input asp-for="DateReceived" class="form-control" type="date" max='' id="dateReceived"/>
                                <span asp-validation-for="DateReceived" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="col">
                            <div class="form-group"> 
                                <label asp-for="IsThisCurrent" class="control-label"></label>
                                @Html.DropDownList("IsThisCurrent", new SelectList(ViewBag.currentlist,"YesNo","YesNo"),"")
                            </div>
                        </div>
                        <br />
                    </div>
                </div>
                <input type="hidden" id="returnUrl" name="returnUrl" value="" />
                <div class="form-group">
                    <input type="submit" name="action" value="Save" class="btn btn-primary" />
                    &nbsp;
                    <button type="submit" name="action" value="SaveAndBack" class="btn btn-primary">Save and go back to the PIF</button>
                    &nbsp;
                    <a class="btn btn-info" asp-action="Index" role="button">Back to List</a>
                </div>
            </form>
        </div>
    </div>
</body>

@section Scripts {
     <script>
        //I use js to allow users to select only the latest date up to today
        function addZero(n) {
            return parseInt(n) >= 10 ? n.toString() : '0' + n;
        }
        let dateNow = new Date(),
            yearNow = dateNow.getFullYear(),
            monthNow = dateNow.getMonth() + 1,
            dayNow = dateNow.getDate(),
            maxDate = yearNow + '-' + addZero(monthNow) + '-' + addZero(dayNow);
        let inp = document.querySelector('#dateReceived');
       
        
        inp.setAttribute('max', maxDate);
    </script>

    <script>
        document.getElementById("returnUrl").value = document.referrer;
    </script>


    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

Fluent Validation:

using FluentValidation;
using Research.Data;

namespace Research.Models
{
    public class DocTrailListsValidator : AbstractValidator<DocTrailList>
    {
        private ResearchContext _context;

        public DocTrailListsValidator(ResearchContext context)
        {
            this._context = context ?? throw new ArgumentNullException(nameof(context));

            RuleFor(x => x.DocumentType).NotNull().WithMessage("A required field");
            RuleFor(x => x.Version).NotNull().WithMessage("A required field");
            RuleFor(x => x.Dated).NotNull().WithMessage("A required field");
            RuleFor(x => x.DateReceived).NotNull().WithMessage("A required field");
            RuleFor(x => x.Version).NotNull().WithMessage("A required field");
            RuleFor(x => x.IsThisCurrent).NotNull().WithMessage("A required field");
            RuleFor(x => x.ProjectID)
                .Must((root, projectID, context) => !IsDuplicate(root))
                .WithMessage("A document of the same type is also marked as current.  This version will not be saved if answered Yes")
                .When(x => x.IsThisCurrent != null && x.IsThisCurrent.Equals("Yes"), ApplyConditionTo.CurrentValidator);
        }

        private bool IsDuplicate(DocTrailList c)
        {
            var existingValues = _context.DocTrailList!.ToList();

            return existingValues.Any(x => x.ProjectID == c.ProjectID &&
                                           x.DocumentType == c.DocumentType &&
                                           x.IsThisCurrent == c.IsThisCurrent);
        }
    }
}

Edit button on Index.cshtml:

<div class="btn-group">
    <a asp-action="Edit" asp-route-id="@item.DetailID">
        <i class="fa fa-edit" style="font-size:24px"></i>
    </a>
    &nbsp;
    <a asp-action="Delete" asp-route-id="@item.DetailID">
        <i class="fa fa-trash-o" style="font-size:24px; color:red"></i>
    </a>
 </div>

Error Message screen: Screen that appears upon clicking the Edit button

7
  • Please share the full exception message: ArgumentNullException: Value cannot be null. (Parameter 'items'), so we can know in which line you get that exception. Thanks. Commented Sep 14 at 1:33
  • I suspect on this line: ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);. Probably you should use _context.PI.ToList() Commented Sep 14 at 1:39
  • Thank you. I changed the above to three different ones and each return the same error message. I placed the following in both GET and POST: ViewData["ProjectID"] = new SelectList(new List<int?> { docTrailList.ProjectID }); ViewData["ProjectID"] = new SelectList(docTrailList.ProjectID); and ViewData["ProjectID"] = _context.PIF != null ? _context.PIF.ToList() : new List<PIF>(); I'll update the question with the error message - it fires at line 11. ViewData["Title"] = "Edit"; on the Edit.cshtml. Commented Sep 14 at 8:24
  • 1
    In your Edit.cshtml, you are using ViewBag.dtnamelist, but in both [HttpGet] and [HttpPost] Edit action, I don't see any of your code with ViewBag.dtnamelist. Probably you mean ViewBag.dtname? Commented Sep 14 at 10:05
  • 1
    Sorry, I also notice that the same issue with ViewBag.currentlist Commented Sep 14 at 10:15

1 Answer 1

0

Aside from the comments mentioned that the variable names conflict in the controller action and the view,

Recommend to have a proper naming indicates the value is a list/array. Also, you can just assign the value directly to the ViewData/ViewBag

ViewData["ProjectIDList"] = new SelectList(_context.PIF!.ToList(), "ProjectID", "ProjectID", docTrailList.ProjectID);

ViewBag.DtnameList = new SelectList(_context.DocType!.ToList(), "DocumentTypeName", "DocumentTypeName");

ViewBag.CurrentList = new SelectList(_context.YesNoList!.ToList(), "YesNo", "YesNo");

And switch to asp-items tag helper for consistency.

<select asp-for="ProjectID" class="form-control" asp-items="ViewBag.ProjectIDList"></select>

<select asp-for="DocumentType" class="form-control" asp-items="ViewBag.DtnameList"></select>

<select asp-for="IsThisCurrent" class="form-control" asp-items="ViewBag.CurrentList"></select>
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. I'll take this on board. Just two further issues to overcome and the 'database' will be ready.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.