Does the DataGrid's IQueryable Data-binding perform asynchronous queries implicitly or do developers have to explicitly call async queries?

Regarding this example,

@using RadzenBlazorDemos.Data
@using RadzenBlazorDemos.Models.Northwind
@using Microsoft.EntityFrameworkCore

@inherits DbContextPage

<RadzenDataGrid AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.Advanced" AllowSorting="true" PageSize="5" AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true"
    Data="@employees" ColumnWidth="300px" LogicalFilterOperator="LogicalFilterOperator.Or" SelectionMode="DataGridSelectionMode.Single" @bind-Value=@selectedEmployees>
    <Columns>
        <RadzenDataGridColumn Property="@nameof(Employee.EmployeeID)" Filterable="false" Title="ID" Frozen="true" Width="80px" TextAlign="TextAlign.Center" />
        <RadzenDataGridColumn Title="Photo" Frozen="true" Sortable="false" Filterable="false" Width="80px" TextAlign="TextAlign.Center" >
            <Template Context="data">
                <RadzenImage Path="@data.Photo" class="rz-gravatar" AlternateText="@(data.FirstName + " " + data.LastName)" />
            </Template>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="@nameof(Employee.FirstName)" Title="First Name" Frozen="true" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.LastName)" Title="Last Name" Width="160px"/>
        <RadzenDataGridColumn Property="@nameof(Employee.Title)" Title="Job Title" Width="200px" />
        <RadzenDataGridColumn Property="@nameof(Employee.TitleOfCourtesy)" Title="Title" Width="120px" />
        <RadzenDataGridColumn Property="@nameof(Employee.BirthDate)" Title="Birth Date" FormatString="{0:d}" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.HireDate)" Title="Hire Date" FormatString="{0:d}" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.Address)" Title="Address" Width="200px" />
        <RadzenDataGridColumn Property="@nameof(Employee.City)" Title="City" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.Region)" Title="Region" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.PostalCode)" Title="Postal Code" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.Country)" Title="Country" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.HomePhone)" Title="Home Phone" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.Extension)" Title="Extension" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Employee.Notes)" Title="Notes" Width="300px" />
    </Columns>
</RadzenDataGrid>

@code {
    IQueryable<Employee> employees;
    IList<Employee> selectedEmployees;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
 
        employees = dbContext.Employees;

        selectedEmployees = new List<Employee>(){ employees.FirstOrDefault() };
    }
}

I just want to confirm that when the RadzenDataGrid implicitly materializes the query for employees, will that query be awaited?

I looked at the code behind for RadzenDataGrid.razor.cs and it appears that all queries are awaited inside the async LoadItems() and InvokeLoadData() methods.

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

namespace Radzen.Blazor
{
    /// <summary>
    /// RadzenDataGrid component.
    /// </summary>
    /// <typeparam name="TItem">The type of the DataGrid data item.</typeparam>
    /// <example>
    /// <code>
    /// &lt;RadzenDataGrid @data=@orders TItem="Order" AllowSorting="true" AllowPaging="true" AllowFiltering="true"&gt;
    ///     &lt;Columns&gt;
    ///         &lt;RadzenDataGridColumn TItem="Order" Property="OrderId" Title="OrderId" /&gt;
    ///         &lt;RadzenDataGridColumn TItem="Order" Property="OrderDate" Title="OrderDate" /&gt;
    ///     &lt;/Columns&gt;
    /// &lt;/RadzenDataGrid&gt;
    /// </code>
    /// </example>
#if NET6_0_OR_GREATER
    [CascadingTypeParameter(nameof(TItem))]
#endif
    public partial class RadzenDataGrid<TItem> : PagedDataBoundComponent<TItem>
    {

        ...

        private async ValueTask<Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>> LoadItems(Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest request)
        {
            var view = AllowPaging ? PagedView : View;
            var top = request.Count;

            if (top <= 0)
            {
                top = PageSize;
            }

            var filter = isOData == true ?
                    allColumns.ToList().ToODataFilterString<TItem>() : allColumns.ToList().ToFilterString<TItem>();
            var loadDataArgs = $"{request.StartIndex}|{top}{GetOrderBy()}{filter}";

            if (lastLoadDataArgs != loadDataArgs)
            {
                await (lastLoadDataTask = InvokeLoadData(request.StartIndex, top));

                lastLoadDataArgs = loadDataArgs;
            }

            var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();

            virtualDataItems = (LoadData.HasDelegate ? Data : itemsToInsert.Count() > 0 ? itemsToInsert.ToList().Concat(view.Skip(request.StartIndex).Take(top)) : view.Skip(request.StartIndex).Take(top))?.ToList();

            return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>(virtualDataItems, totalItemsCount);
        }

        ...

        internal async Task InvokeLoadData(int start, int top)
        {
            var orderBy = GetOrderBy();

            Query.Skip = skip;
            Query.Top = PageSize;
            Query.OrderBy = orderBy;

            Query.GetFilter = () => allColumns.ToList().ToFilterString<TItem>();

            filters = allColumns.ToList()
                .Where(c => c.Filterable && c.GetVisible() && (c.GetFilterValue() != null
                    || c.GetFilterOperator() == FilterOperator.IsNotNull || c.GetFilterOperator() == FilterOperator.IsNull
                    || c.GetFilterOperator() == FilterOperator.IsEmpty | c.GetFilterOperator() == FilterOperator.IsNotEmpty))
                .Select(c => new FilterDescriptor()
                {
                    Property = !string.IsNullOrEmpty(c.FilterProperty) && c.FilterProperty != c.Property ? c.Property : c.GetFilterProperty(),
                    FilterProperty = !string.IsNullOrEmpty(c.FilterProperty) && c.FilterProperty != c.Property ? c.FilterProperty : null,
                    FilterValue = c.GetFilterValue(),
                    FilterOperator = c.GetFilterOperator(),
                    SecondFilterValue = c.GetSecondFilterValue(),
                    SecondFilterOperator = c.GetSecondFilterOperator(),
                    LogicalFilterOperator = c.GetLogicalFilterOperator(),
                    Type = c.Type
                })
                .ToList();

            Query.Filters = filters;
            Query.Sorts = sorts;
            if (LoadData.HasDelegate)
            {
                await LoadData.InvokeAsync(new Radzen.LoadDataArgs()
                {
                    Skip = start,
                    Top = top,
                    OrderBy = orderBy,
                    GetFilter = () => IsOData() ? allColumns.ToList().ToODataFilterString<TItem>() : allColumns.ToList().ToFilterString<TItem>(),
                    Filters = filters,
                    Sorts = sorts
                });
            }
        }
    }
}

No, View is what is used for IQueryable support. If you need to await you can indeed use LoadData event instead.

1 Like