Search code examples
c#.net-corerazorasp.net-core-8

ASP.NET Core 8.0 Razor - NullReferenceException when looping through Model


I am trying to build a simple app that iterates through 'Car' (model) names.

I am getting the following error when trying to iterate through items called through an API.

enter image description here

Index.cshtml:

@page
@model CarPageModel

<div class="text-center">
    <h1 class="display-4">Cars</h1>
    <hr/>
    @foreach (var c in @Model.Cars) {
    <span>@c.Name</span>
    } 
</div>

Index.cshtml.cs

Note: The API call works and returns data when entering debug mode.

using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace CarApp.Pages;

public class CarPageModel : PageModel
{
    private readonly ILogger<CarPageModel> _logger;

    public CarPageModel(ILogger<CarPageModel> logger)
    {
        _logger = logger;
    }

    // Car is a Model
    public List<Car> Cars { get; set; } = default!;

    public async void OnGetAsync()
    {
        using(var httpClient = new HttpClient()) {

            // Call controller api
            var uri = new Uri("http://localhost:5009/api/cars");

            using var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);

            response.EnsureSuccessStatusCode();

            var stream = await response.Content.ReadAsStreamAsync();

            var serializerOptions = new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            }; 
            var data = await JsonSerializer.DeserializeAsync<List<Car>>(stream, serializerOptions);
            Cars = data == null ? [] : data;
        }
    }

}

Program.cs

using Microsoft.EntityFrameworkCore;
using CarApp;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

// Add appsettings.json
builder.Configuration.AddJsonFile("appsettings.json",
        optional: true,
        reloadOnChange: true);

var connectionString = builder.Configuration.GetConnectionString("AppDatabaseCS");

builder.Services.AddDbContext<CarContext>(options =>
    options.UseSqlServer(connectionString));

builder.Services.AddControllers();
builder.Services.AddHttpClient();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.MapControllers();

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

I can provide additional code if needed. I just didn't think the controller / model class was relevant but I can provide if needed.


Note: I took a look at similar questions and they are saying to add 'return View()' but I don't know where to inject that code in my solution. But I don't know if that is relevant to my issue.


Solution

  • The error is telling you that Cars is null. And the code initializes it to null:

    public List<Car> Cars { get; set; } = default!;
    

    Instead, initialize it to an empty list:

    public List<Car> Cars { get; set; } = new List<Car>;
    

    That way the loop will just silently iterate over 0 records.


    Additionally, this is almost certainly wrong:

    public async void OnGetAsync()
    

    Using void makes this method un-awaitable. So if the expectation is that the framework will be able to wait until Cars is populated before continuing, this may prevent that. Return a Task instead:

    public async Task OnGetAsync()