
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 ofRunInitAndSetParametersAsync
method inComponentBase
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.
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!
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 theEither
. This will allow you to account for a not foundPerson
. This relies on using a NuGet package LanguageExt which enables this.