Search code examples
asp.net-corekendo-uikendo-asp.net-mvckendo-asp.net-core

Can using async/await in a razor page's OnGet cause unreliable loading of controls?


I switched the OnGet of my razor page to be async and I noticed some of my controls would no longer load reliably.

I have a kendo multiselect on the page and once I switched to async it only loads the list of options about 50% of the time. Controls that don't contain a list of options seem to load ok. I switched back to synchronous and everything loads reliably again. Is async/await not appropriate for use when fetching data from the DB to populate the page?

Here's the async version of my OnGet:

    public async void OnGet(int securityGroupId)
    {
        if (securityGroupId == -1)
        {
            SecurityGroup = new SecurityGroup();
            SecurityGroup.IsActive = true;
        }
        else
        {
            SecurityGroup = await DL.GetEntityByIdAsync<SecurityGroup, int>(securityGroupId);
        }

        RoleList = await DL.GetLookupListAsync<Role>();
        RoleList = RoleList.OrderBy(r => r.Text).ToList();
    }

Here's the synchronous version:

    public void OnGet(int securityGroupId)
    {
        if (securityGroupId == -1)
        {
            SecurityGroup = new SecurityGroup();
            SecurityGroup.IsActive = true;
        }
        else
        {
            SecurityGroup = DL.GetEntityById<SecurityGroup, int>(securityGroupId);
        }

        RoleList = DL.GetLookupList<Role>().OrderBy(r => r.Text).ToList();
    }

RoleList is: public List<DDL> RoleList { get; set; } DDL is a class containing a string Id and a string Text;

Here's the async version of GetEntityById:

    public async Task<TEntity> GetEntityByIdAsync<TEntity, TId>(TId id) where TEntity : class, IIdentifiable<TId>, new()
                                                                   where TId : IEquatable<TId>
    {
        var entity = new TEntity();

        try
        {
            entity = await db.Set<TEntity>().FirstOrDefaultAsync(t => t.Id.Equals(id));
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return entity;
    }

Here's the synchronous version:

    public TEntity GetEntityById<TEntity, TId>(TId id) where TEntity : class, IIdentifiable<TId>, new()
                                                       where TId : IEquatable<TId>
    {
        var entity = new TEntity();

        try
        {
            entity = db.Set<TEntity>().FirstOrDefault(t => t.Id.Equals(id));
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return entity;
    }

Here's the async version of GetLookupList:

    public async Task<List<DDL>> GetLookupListAsync<TEntity>() where TEntity : class, IDDLable
    {
        List<DDL> ddl = new List<DDL>();

        try
        {
            ddl = await db.Set<TEntity>().Select(lu => new DDL { Id = lu.DDLId, Text = lu.DDLText }).ToListAsync();
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return ddl;
    }

Here's the synchronous version:

    public List<DDL> GetLookupList<T>() where T : class, IDDLable
    {
        List<DDL> ddl = new List<DDL>();

        try
        {
            ddl = db.Set<T>().Select(lu => new DDL { Id = lu.DDLId, Text = lu.DDLText }).ToList();
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return ddl;
    }

The multiselect is defined as:

        @(Html.Kendo().MultiSelectFor(m => m.SelectedRoles)
            .DataValueField("Id")
            .DataTextField("Text")
            .AutoClose(false)
            .Placeholder("Select Role(s)...")
            .BindTo(Model.RoleList))

Solution

  • Changing the return type of OnGet from void to Task resolved the issue. The Async suffix didn't have any effect, I believe it's just a matter of naming convention.

    The function now reads:

        public async Task OnGetAsync(int securityGroupId)
        {
            if (securityGroupId == -1)
            {
                SecurityGroup = new SecurityGroup();
                SecurityGroup.IsActive = true;
            }
            else
            {
                SecurityGroup = await DL.GetEntityByIdAsync<SecurityGroup, int>(securityGroupId);
                SelectedRoles = SecurityGroup.RoleIdList;
            }
    
            RoleList = await DL.GetLookupListAsync<Role>();
            RoleList = RoleList.OrderBy(r => r.Text).ToList();
        }