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" />
<button type="submit" name="action" value="SaveAndBack" class="btn btn-primary">Save and go back to the PIF</button>
<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>
<a asp-action="Delete" asp-route-id="@item.DetailID">
<i class="fa fa-trash-o" style="font-size:24px; color:red"></i>
</a>
</div>

ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);. Probably you should use_context.PI.ToList()ViewBag.dtnamelist, but in both[HttpGet]and[HttpPost]Edit action, I don't see any of your code withViewBag.dtnamelist. Probably you meanViewBag.dtname?ViewBag.currentlist