Why is my Blazor Server App waiting to render until data has been retrieved, even when using async?

Viewed 169

I'm building a Blazor Server app using .NET 6. As an example I have a list of customers that I retrieve from a Sqlite db using Entityframework Core. I want to show a loading indicator when the data is still being retrieved. Like so:

@page "/customers"
@inject CustomerService CustomerService


@if(customers.Count == 0)
{
    <p>Loading...</p>
}
else
{
    <table>
        @foreach(Customer customer in customers)
        {
            <tr>
                <td>
                    @customer.Id
                </td>
                <td>
                    @customer.Name
                </td>
            </tr>
        }
    </table>
}

When I use following data implementation it works fine:

private List<Customer> customers = new();

protected async override Task OnInitializedAsync()
{
    await Task.Run(() =>
    {
        this.customers = CustomerService.GetCustomers();
    });
}

However when I use following data implementation it doesn't work. Meaning that the entire page only becomes visible once the data has been loaded (white page while waiting).

private List<Customer> customers = new();

protected async override Task OnInitializedAsync()
{
    this.customers = await CustomerService.GetCustomersAsync();
}

CustomerService.cs

    public class CustomerService
    {
        private CustomerContext _customerContext;

        public CustomerService(CustomerContext customerContext)
        {
            _customerContext = customerContext;
        }

        public async Task<List<Customer>> GetCustomersAsync()
        {
            return await _customerContext.Customers.ToListAsync();
        }

        public List<Customer> GetCustomers()
        {
            return _customerContext.Customers.ToList();
        }
    }

I would expect my second implementation to also work asynchronously. Could anyone help me understand please? What am I missing here? Thanks in advance for the help!

1 Answers

It depends on your database whether ToListAsync() is really and always async. It may execute synchronously, depending on the provider, caching etc.

There is a simple fix:

private List<Customer> customers = new();

protected async override Task OnInitializedAsync()
{
   await Task.Delay(1);
   this.customers = await CustomerService.GetCustomersAsync();
}

The async Delay() will allow the loading... message to be displayed.

Note that you ought to call GetCustomers() without the Task.Run() though.

... from a Sqlite db

Here is an answer about sqlite.. It does not actually support async operations. The EF provider just pretends.

Related