Unacceptable

More than once I’ve been stumped by an error in Blazor with very little help from the stack trace. Even when debugging and stepping through the code it seemingly breaks out of nowhere.

In this post I would like to discuss a common reason why this occurs.

Here is a live example of such an error Internal Server Error (aryehsilver.co.uk):

Let’s first go through the code to see what we’re trying to do.

I have setup a basic Person model that is added to a SQLight database.
Then in the Index.razor.cs file we start by injecting the AppDbCOntext and in the OnInitializedAsync method migrate the database to ensure we don’t get an error trying to find it. Then we try and populate the People list with data from the database. This is never null since we initialise it with new(). For the effect we add a delay and then create a new Person which will be used in the razor.

When running this we get the error as in the above example.

NullReferenceException: Object reference not set to an instance of an object.

If you run the code (download the demo source code zip file or view on GitHub) and step through, you will hit line 18 (Index.razor.cs) which will populate the data. Then when stepping over to the next line we get the NRE.

Explanation

The reason for this error is due to the renderer trying to render the view before the OnInitializedAsync method has finished, which means that the Person (used in the Index.razor to display the fields) is still null and therefore throws the error.

This occurs since we have used an async method so the method continues to run without waiting for the result.

Call state has changed here so that we render after the sync part of OnInitAsync has run and wait for it to finish before we continue. If no async work has been done yet, we want to defer calling StateHasChanged up until the first bit of async code happens or until the end. Additionally, we want to avoid calling StateHasChanged if no async work is to be performed.

Comment found at beginning of RunInitAndSetParametersAsync method in ComponentBase

This means when we get to await Task.Delay(10000); the renderer is trying to show the UI before the method has finished, so Person is still null and we therefore hit the NRE.

This explanation has been based on the answer to this StackOverflow question.

Solution

The simplest solution is to add a local private bool Render; variable that is set to true only when finishing the method. Then in the markup wrap the entire <EditForm> in @if (Render) which will then only show the UI once finishing the OnInitializedAsync method.

So the Index.razor.cs would look like this:

private bool Render;

protected override async Task OnInitializedAsync() {
  using (AppDbContext context = new()) {
    context.Database.Migrate();
  }
  People = await Context.People.ToListAsync();
  await Task.Delay(10000);
  Person = new() {
    Id = 1,
    Name = "New person",
    Tel = 05654534674
  };
  Render = true;
}

and the Index.razor would look like this:

@if (Render) {
  <EditForm Model="People">
    <div class="row">
      <div class="col-md-8">
        <p>
          <label>
            Person list
            <InputSelect @bind-Value="PersonId">
              @foreach (Person person in People) {
                <option value="@person.Id">@person.Name</option>
              }
            </InputSelect>
          </label>
        </p>

        <p>
          <label>
            Person name
            <InputText @bind-Value="Person.Name" />
          </label>
        </p>

        <p>
          <label>
            Person tel
            <InputNumber @bind-Value="Person.Tel" />
          </label>
        </p>
      </div>
    </div>
  </EditForm>
}

You can see this live here Blazor Render Error Test (aryehsilver.co.uk).
View source code on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.