-2

My code is passing a form on submit to the database. The form shows the customer which is run through a selectlist using IEnumerable, and if a customer site is available it will show that as well through some JS code (hide/show when status changes).

Using breakpoints I see that in my view 'Model.Checklists.CustomerId' is null, but it shouldn't be null, looking in the asp-for the correct integer is shown.

<select asp-for="@Model.Checklists.CustomerId" id="CustomerId" asp-items="Model.Customer.ToSelectListItem(Model.Checklists.CustomerId)" class="form-control"></select>

Image: enter image description here enter image description here

The error that's being thrown: ArgumentNullException: Value cannot be null. (Parameter 'source')

Stacktrace:

        ArgumentNullException: Value cannot be null. (Parameter 'source')
    System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
    System.Linq.Enumerable.Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
    Checklist.Extensions.IEnumerableExtension.ToSelectListItem<TSource>(IEnumerable<TSource> items, int selectedValue) in IEnumerableExtension.cs
    +
                return from item in items
    AspNetCore.Areas_Sales_Views_Checklist_Create.<ExecuteAsync>b__29_0() in Create.cshtml
    +
                <select asp-for="@Model.Checklists.CustomerId" id="CustomerId" asp-items="Model.Customer.ToSelectListItem(Model.Checklists.CustomerId)" class="form-control"></select>
    Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
    Microsoft.AspNetCore.Mvc.TagHelpers.RenderAtEndOfFormTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
    Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
    AspNetCore.Areas_Sales_Views_Checklist_Create.ExecuteAsync() in Create.cshtml
    +
        ViewData["Title"] = "Checklist Aanmaken";
    Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
    Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
    Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
    Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
    Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
    Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
    Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
    Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
    Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
    Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

IEnumerableExtensions.cs:

using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Checklist.Extensions
{
    public static class IEnumerableExtension
    {
        public static IEnumerable<SelectListItem> ToSelectListItem<T>(this IEnumerable<T> items, int selectedValue)
        {
            return from item in items
                    select new SelectListItem
                    {
                        Text = item.GetPropertyValue("Name"),
                        Value = item.GetPropertyValue("Id"),
                        Selected = item.GetPropertyValue("Id").Equals(selectedValue.ToString())
                    };
        }

ChecklistViewModel.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Checklist.Models.ViewModels
{
    public class ChecklistViewModel
    {
        public ChecklistModel Checklists { get; set; }
        public IEnumerable<Customer> Customer { get; set; }
        public IEnumerable<CustomerSite> CustomerSite { get; set; }

        public IEnumerable<IdentityUser> User { get; set; }
        public ChecklistModel.EDeviceCategory DeviceCategory { get; set; }

    }
}

ChecklistModel.cs:

[Display(Name = "Customer")]
public int CustomerId { get; set; }

[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }

ChecklistController.cs:

using Checklist.Data;
using Checklist.Models;
using Checklist.Models.ViewModels;
using Checklist.Utility;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Checklist.Areas.Sales.Controllers
{
    [Area("Sales")]
    public class ChecklistController : Controller
    {
        private readonly ApplicationDbContext _db;
        private readonly IWebHostEnvironment _hostingEnvironment;
        private readonly IEmailSender _emailSender;

        [BindProperty]
        public ChecklistViewModel ChecklistVM { get; set; }

        public ChecklistController(ApplicationDbContext db, IWebHostEnvironment hostingEnvironment, IEmailSender emailSender)
        {
            _db = db;
            _hostingEnvironment = hostingEnvironment;
            _emailSender = emailSender;

            ChecklistVM = new ChecklistViewModel()
            {
                Customer = _db.Customer,
                Checklists = new Models.ChecklistModel(),
                CustomerSite = _db.CustomerSite,
                User = _db.IdentityUser

            };
        }

        ......

        // GET - CREATE
        public IActionResult Create()
        {
            return View(ChecklistVM);
        }

        [HttpPost, ActionName("Create")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> CreatePOST()
        {

            if (int.TryParse(Request.Form["CustomerSiteId"], out int customerSiteId))
            {
                ChecklistVM.Checklists.CustomerSiteId = Convert.ToInt32(Request.Form["CustomerSiteId"].ToString());
            }

            
            ChecklistVM.Checklists.EmployeeId = Request.Form["EmployeeId"].ToString();
            

            var claimsIdentity = (ClaimsIdentity)User.Identity;
            var claims = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);

            ChecklistVM.Checklists.SalesPerson = _db.ApplicationUser.Where(u => u.Id == claims.Value).FirstOrDefault().Name;


            ChecklistVM.Checklists.Employee = (_db.ApplicationUser.Where(u => u.Id == Request.Form["EmployeeId"].ToString()).FirstOrDefault().Name);

            

            if (!ModelState.IsValid) 
            {
                return View(ChecklistVM);
            }



            

            _db.Checklist.Add(ChecklistVM.Checklists);
            await _db.SaveChangesAsync();

            return RedirectToAction(nameof(Index));
        }

Create.cshtml:

<div class="form-group row">
    <div class="col-2">
        <label asp-for="Checklists.CustomerId" class="col-form-label-lg">Customer</label>
    </div>
    <div class="col-5">
        <select asp-for="@Model.Checklists.CustomerId" id="CustomerId" asp-items="Model.Customer.ToSelectListItem(Model.Checklists.CustomerId)" class="form-control"></select>
    </div>
</div>

How can I find out where this issue is coming from?

The strange thing is that it has worked on my machine earlier and I haven't touched any of this code for a while.

Dein
  • 19
  • 5
  • 2
    `I am still trying to learn asp.net` - the IEnumerable error has nothing to do with ASP.NET. You have passed `null` to `ToSelectListItem` in the tag, which means your `public IEnumerable Customer { get; set; }` is `null` at that point. – GSerg Sep 25 '21 at 19:08
  • I placed some breakpoints and it is indeed null. But why I wonder, it has worked in the past. – Dein Sep 25 '21 at 19:55

1 Answers1

0

The culprit seems to be in my createPOST() method.

More particular this if statement:

if (!ModelState.IsValid) 
{
    return View(ChecklistVM);
}

When commenting out this code, all seems to work as expected. Data is created in the database and I get returned to the index page, which displays the newly created item.

So I can conclude that my modelstate is invalid and it returns the view with a model that has null values, and those cause the selectlistitem to fail loading.

The only thing now is figuring out why the modelstate if statements gets triggered...

Edit:

It doesn't make sense now, I saw that I forgot some asp-validation-for tag helpers, so I created those. I then uncommented the modelstate if statement, and tried once again. That seems to have resolved this issue. To further investigate I commented out the validation tags, and tried once again, and it still works... (cleared cache, cookies,...)

EDIT:

The root cause seems to be that I didn't create my DeviceCategory in my ViewModel, which caused the model to be invalid when POST.

public ChecklistModel.EDeviceCategory DeviceCategory { get; set; }
Dein
  • 19
  • 5