Using Skip/Take Grid Displays No Records

1 Answer 135 Views
Grid
Mike
Top achievements
Rank 1
Iron
Mike asked on 27 May 2023, 03:42 PM

I have a grid defined as such:

@(Html.Kendo().Grid<AuditViewModel>()
    .Name("AuditLogGrid")
    .Columns(columns =>
    {
        columns.Bound(c => c.Id).Hidden(true);
        columns.Bound(c => c.UserId).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.TableName).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.AuditType).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.KeyValuesValue).Encoded(false).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.OldValuesValue).Encoded(false).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.NewValuesValue).Encoded(false).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
        columns.Bound(c => c.ChangedColumnsValue).HtmlAttributes(new { @class = "k-text-right", style = "vertical-align: text-top" });
    })
    .Pageable(pager => pager.Refresh(true))
    .Sortable()
    .Filterable()
    .NoRecords()
    .DataSource(dataSource => dataSource
        .Ajax()
        .Batch(true)
        .PageSize(10)
        .ServerOperation(true)
        .Events(events => events.Error("error_handler"))
        .Model(model =>
        {
            model.Id(p => p.Id);
            model.Field(p => p.UserId);
            model.Field(p => p.TableName);
            model.Field(p => p.AuditType);
            model.Field(p => p.KeyValuesValue);
            model.Field(p => p.OldValuesValue);
            model.Field(p => p.NewValuesValue);
            model.Field(p => p.ChangedColumnsValue);
        })
        .Read(read => read.Action("GetAuditLogs", "Audit").Data("forgeryToken"))
    ))

And in my "GetAuditLogs" controller methods, I pass in the "request.Page" and "request.PageSize" to calculate Skip and Take.  The EF query call DOES return records (default is set to 10 records) however when returning from the Controller, the Grid displays "No Records"

My controller method is as follows:


[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> GetAuditLogsAsync([DataSourceRequest] DataSourceRequest request)
    {
        _logger.LogDebug("GetAuditLogsAsync()");
        try
        {
            var logs = await _auditService.GetAsync(null, q => q.OrderByDescending(x => x.DateTime),
                string.Empty, request.Page, request.PageSize);
            var total = await _auditService.CountAsync(null, string.Empty);
            var models = logs.Select(x => new AuditViewModel
            {
                Id = x.Id,
                UserId = x.UserId,
                DateTime = x.DateTime,
                TableName = x.TableName,
                AuditType = x.Type,
                KeyValues = string.IsNullOrWhiteSpace(x.PrimaryKey) ? new Dictionary<string, object>() :
                    JsonSerializer.Deserialize<Dictionary<string, object>>(x.PrimaryKey),
                OldValues = string.IsNullOrWhiteSpace(x.OldValues) ? new Dictionary<string, object>() :
                    JsonSerializer.Deserialize<Dictionary<string, object>>(x.OldValues),
                NewValues = string.IsNullOrWhiteSpace(x.NewValues) ? new Dictionary<string, object>() :
                    JsonSerializer.Deserialize<Dictionary<string, object>>(x.NewValues),
                ChangedColumns = string.IsNullOrWhiteSpace(x.AffectedColumns) ? new List<string>() :
                    JsonSerializer.Deserialize<List<string>>(x.AffectedColumns)
            });

            var result = await models.ToDataSourceResultAsync(request);
            result.Total = total;
            return Json(result);
        }
        catch (Exception ex)
        {
            _logger.LogError($"GetAuditLogsAsync() | error [{ex}]", ex);
            throw;
        }
    }

And the Audit Service "GetAsync" looks like:


public async Task<IEnumerable<Data.Entities.Audit>> GetAsync(
        Expression<Func<Data.Entities.Audit, bool>>? filter = null,
        Func<IQueryable<Data.Entities.Audit>, IOrderedQueryable<Data.Entities.Audit>>? orderBy = null,
        string includeProperties = "",
        int page = 0,
        int pageSize = 0)
    {
        IQueryable<Data.Entities.Audit> query = _dbSet;
        if (filter != null)
        {
            query = query.Where(filter);
        }

        if (!string.IsNullOrWhiteSpace(includeProperties))
        {
            query = includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                .Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
        }

        var result = orderBy != null
            ? orderBy(query)
            : query;

        if (page > 0 && pageSize > 0)
        {
            result = result.Skip((page - 1) * pageSize).Take(pageSize);
        }

        return await result.ToListAsync();
    }

I have verified that the returned results ARE correct (based on Id's), but the Grid displays "No Records".  I'd rather not return the ENTIRE dataset to the grid as it is quite large (and the models can be large - this is for auditing so it contains EVERYTHING that was modified).  Is there something I'm missing?

1 Answer, 1 is accepted

Sort by
0
Accepted
Mike
Top achievements
Rank 1
Iron
answered on 28 May 2023, 06:13 AM

I'm going to answer my own question.  I found the issue.  I have to explicitly create a new DataSourceResult object, I can't use .ToDataSourceResultAsync(request).

My method now ends with:

//var result = await models.ToDataSourceResultAsync(request);
var result = new DataSourceResult
{
     Data = models,
     Total = total
};
return Json(result);
Maybe it's a bug with the .ToDataSourceResult method, or maybe not - I'm not to sure.  Regardless, I solved my issue
Aleksandar
Telerik team
commented on 31 May 2023, 08:29 AM

Hi Mike,

The observed is indeed the expected behavior. The ToDataSourceResult() and ToDataSourceResultAsync() methods are used to page, sort, filter, and group the collection that is passed to it. As noted in the documentation, if this collection is already paged, the method returns an empty result. When performing custom Ajax binding instantiate a new DataSourceResult object, as you already have, and pass the data to it. We have demonstrated this in the Custom Ajax Binding for the Demo for the Grid (click on the View Source tab and inspect the Controller).

Mike
Top achievements
Rank 1
Iron
commented on 31 May 2023, 03:20 PM

Ahh, I missed that part of the documentation.  Thanks for the update!
Tags
Grid
Asked by
Mike
Top achievements
Rank 1
Iron
Answers by
Mike
Top achievements
Rank 1
Iron
Share this question
or