Unable to bind on server side

Hi,

I have a Radzen datagrid. The paging works okay but the numbers underneath do not update.

E.g. if I press 2. It'll show page 2's data but the actual pager control still says it's on page 1.

The code is pretty straightforward.

    var results = await _blRepo.Get(args.Skip.Value, args.Top.Value);
    domains = results.Item2.AsODataEnumerable();
    count = results.Item1;

I set the domains and count and it should just handle the rest? The repo method calls a SQL stored procedure which returns the paged data.

Thanks,

AsODataEnumerable is probably not required. Make sure that count has the correct value - it should be the total number of items (without paging).

Thank you.

I have removed that but still the same issue. The count is the total count. It does change the paged data, just doesn't change the paginator page number.

The grid definition looks like this:

        <RadzenDataGrid @ref="domainsGrid" AllowFiltering="true" AllowColumnResize="true" FilterMode="FilterMode.Advanced" AllowSorting="true" PageSize="50" AllowPaging="true"   PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true" 
        Data="@domains" LoadData="@LoadDomainsAsync" IsLoading="@dataloading" Count="@count" TItem="DomainLookupTool.Data.Blacklist" ColumnWidth="300px" LogicalFilterOperator="LogicalFilterOperator.Or" EditMode="DataGridEditMode.Single" RowUpdate="@OnUpdateRow" RowCreate="@OnCreateRow" >

Anything on there scream out as wrong? It's server side-blazor, i.e. not webassembly does that matter?

Can you show the full code in LoadDomainsAsync? Everything else seems fine. Still you can try commenting out the other RadzenDataGrid attributes to see if it makes any difference.

async Task LoadDomainsAsync(LoadDataArgs args)
{
    loading = true;
    dataloading = true;

    StateHasChanged();
    await Task.Delay(1);

    var results = await _blRepo.Get(args.Skip.Value, args.Top.Value);
    domains = results.Item2;
    count = results.Item1;

    loading = false;
    dataloading = false;

    StateHasChanged();
    await Task.Delay(1);
}



    public async Task<(int, IQueryable<Blacklist>)> Get(int pageNumber, int pageSize = 50)
    {
        var param = new DynamicParameters();
        param.Add("@Skip", pageNumber);
        param.Add("@Take", pageSize);

        using (var conn = new SqlConnection(_config.GetConnectionString("DefaultConnection")))
        {
            try
            {
                await conn.OpenAsync();

                var results = await conn.QueryMultipleAsync("dbo.GetBlacklist", param: param, transaction: null, commandType: System.Data.CommandType.StoredProcedure);
                var blacklist = results.Read<Blacklist>().AsQueryable();
                int count = blacklist.Count() > 0 ? blacklist.FirstOrDefault().Count : 0;

                return (count, blacklist);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Could not retrieve blacklist: {ex.Message}");
            }
            finally
            {
                await conn.CloseAsync();
            }
        }

        return (0, null);
    }

The count is always the total number of records and each time I bind to the grind it will show 50. (The page size) in the bound page size.

You seem to return count after applying paging. What is the actual value of count? It should not be 50 but the number of all rows in the table or view.

Another thing you can try is remove all await Task.Delay(1) StateHasChanged() calls.

Count is the total number of records in the table i'm querying. In this example it's around 60,000. Item1 contains the count.

Item2 is the paged list, this contains 50 records to display on the current page.

I've switched it around, doesn't make any difference. Just to clarify though, the data will change, just the numbers at the bottom of the paginator don't, it's always stuck on page 1.

async Task LoadDomainsAsync(LoadDataArgs args)
{
    loading = true;
    dataloading = true;

    //StateHasChanged();
    //await Task.Delay(1);

    var results = await _blRepo.Get(args.Skip.Value, args.Top.Value);
    count = results.Item1;
    domains = results.Item2;


    loading = false;
    dataloading = false;

    //StateHasChanged();
    //await Task.Delay(1);
}

Is there a way to just set what page I'm on programmatically? I tried setting CurrentPage but it doesn't change the paginator controls selected page.

There shouldn't be need to change the current page from code. Have you tried our LoadData demo? You can run it in Blazor Server mode locally by opening the RadzenBlazorDemos.Server.sln.

I have yes.

The problem with the demo you have on there is that you load the entire dataset into memory. I have millions of records on other pages so it's slow / crashes.

I use a SQL stored procedure to return the results I want. This means I don't perform a skip or take on the result here, it's already done in the stored procedure.

Is this not possible with your controls?

EDIT:

Even removing the pagination in the stored procedure it still won't update the pagination control.

E.g. changing it to this, which is more inline to what you have in your demo project (_blRepo args are no longer in the SP, just passing them through to save time):

    var results = await _blRepo.Get(args.Skip.Value, args.Top.Value);
    count = results.Item1;
    domains = results.Item2.Skip(args.Skip.Value).Take(args.Top.Value).ToList();

That should not be the case. We are using this

var query = dbContext.Employees.AsQueryable();

and then

// Important!!! Make sure the Count property of RadzenDataGrid is set.
count = query.Count();

// Perform paging via Skip and Take.
employees = query.Skip(args.Skip.Value).Take(args.Top.Value).ToList();

Nothing here should load the entire dataset into memory.

It is possible and demonstrated in that demo. Something in your code seems to cause a problem but I can't pinpoint it without reproducing it somehow. This is why I asked you to try our demo or delete all RadzenDataGrid properties without Data, Count and LoadData.

Hi Korchev,

Thanks for digging into this.

I did have the paging working until we moved to handling the result size ourselves. The problem I had though was on tables with millions of records it would fall over. So we created a stored procedure which just returns the page size number (50 in our case) of records.

Since then I can press the page buttons and the data will change but the page numbers will never move.

This is what I have in my datagrid definition now.

        <RadzenDataGrid @ref="domainsGrid" Data="@domains" LoadData="@LoadDomainsAsync" Count="@count" AllowPaging="true" ShowPagingSummary="true">

It's been stripped down heavily. Same issue unfortunately. My stored procedure will only return 50 records at a time. It also returns the total row count (60,000). This is then passed into their associated parameters, count & data. I must be missing something fundamental?

I'm not using entityframework or Linq to get the data.

I tested this and it seems to work as expected.

<RadzenDataGrid style="height: 335px"IsLoading=@isLoading Count="@count" Data="@employees" LoadData="@LoadData" AllowPaging="true" PageSize="50">
    <Columns>
        <RadzenDataGridColumn TItem="DataItem" Property="Id" Title="Id" />
        <RadzenDataGridColumn TItem="DataItem" Property="Text" Title="Text" />
    </Columns>
</RadzenDataGrid>


@code {
    int count;
    IEnumerable<DataItem> employees;
    bool isLoading = false;

    class DataItem
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }

    Task<IEnumerable<DataItem>> GetAsync(int skip, int take)
    {
        var items = new List<DataItem>();
        for (int i = skip; i < skip + take; i++)
        {
            items.Add(new DataItem { Id = i, Text = $"Item {i}"});
        }

        return Task.FromResult((IEnumerable<DataItem>)items);
    }

    async Task LoadData(LoadDataArgs args)
    {
        isLoading = true;

        await Task.Yield();

        count = 60000;

        // Perform paginv via Skip and Take.
        employees = await GetAsync(args.Skip.Value, args.Top.Value);

        isLoading = false;
    }
}

load-data

Try it and let me know if it works for you!

Thanks Korchev, this only works on my datasets that use the DBContext and can cast as QueryAble.

If I want to attach this to a stored procedure it doesn't work. I'm going to leave this here for now, but happy to go into more detail if that's helpful.

Did you try my demo from my previous post? It doesn't use Linq at all. I am not sure how using a stored procedure could interfere with the paging.

Yeah I did, it works okay if I follow it exactly (which I have done on 2 out of the 3 of the pages I serve these grids on).

As soon as I put in a stored procedure where the paging is done inside the stored procedure, the page buttons will never change from 1, even though it pages through the data and I set the total record count.

How does the stored procedure look like? The only thing it can bring is a delay.

I updated the demo to use the following procedure (with added delay) and again it works as expected.

    async Task<IEnumerable<DataItem>> GetAsync(int skip, int take)
    {
        await Task.Delay(1000);

        var items = new List<DataItem>();
        for (int i = skip; i < skip + take; i++)
        {
            items.Add(new DataItem { Id = i, Text = $"Item {i}"});
        }

        return items;
    }

Maybe the last option is to attach the source code of the Radzen.Blazor components to your application and debug the pager code.

Hi Korchev,

This is the SP, removed a couple of columns but pretty much identical:

CREATE OR ALTER PROCEDURE [dbo].[GetBlacklist]
(
@Skip INT = 0,
@Take INT = 50
)
AS
BEGIN
SET NOCOUNT ON;

DECLARE @count int;

SET @count = (SELECT COUNT(Id) FROM dbo.Blacklist)

SELECT  Id,
		DomainName, 
		@count AS [Count]
FROM dbo.Blacklist
ORDER BY DomainName
OFFSET @Skip ROWS FETCH NEXT @Take ROWS ONLY;

END
GO

That would bring up back to the original source in C#.

var results = await _blRepo.Get(args.Skip.Value, args.Top.Value);
domains = results.Item2.AsEnumerable();
count = results.Item1;

Item1 contains the total record count, item2 is 50 items (the page size).