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 assign the Person to the first person in the People list, which is then 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.

The explanation is based on this answer in StackOverflow.

The Simplest 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(1000);
  Person = _people.FirstOrDefault();
  _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 the source code on GitHub.

Another simple solution is to initialise Person to new() so there is never a null Person. The issue with this approach is that generally you don’t want to show the form empty and then populate it with data. If the operation to get the data takes some time, the user may not realise what is going on behind. Therefore it is better to show a loader whilst you’re retrieving the data.

2 Comments

  • Gabe

    I am new to Blazor and ran in to this issue several times before I “discovered” that the OnInitializedAsync method calls render after it encounters the first “await”. Yikes. I spent about an hour trying to figure out how to block execution of the render until the end of the method and your simple solution should have been obvious! Thank you for sharing your solution to this issue!

    • Aryeh Silver

      Hi Gabe, It took me a while to work it out too… One of the joys of programming…
      A better solution is actually to use a functional style where you would have an Either which will be one of two possibilities (i.e. either a person object or a none object). Then in the view you show UI depending on the state of the Either. This will allow you to account for a not found Person. This relies on using a NuGet package LanguageExt which enables this.

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.