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

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!



Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1