Skip and Take not working for an IQueryable datasource
Asked Answered
H

2

8

I have a common method across all objects in my service layer named "GetBaseEntity". This basically has a definition of:

public IQueryable<TEntity> GetBaseEntity(bool includeNavigation = true, bool isAdmin = false)
{
    var objBase = _context.EntityName;

    if (isAdmin)
    {
        return objBase;
    }

    return objBase.Where(x => x.IsActive == true && x.IsDeleted == false);
}

This returns an IQueryable of type TEntity. I want to dynamically pass the pagination options in a method so I implemented it this way:

public async Task<IEnumerable<EntityDto>> LoadResources(PagingOptions pagingOptions)
{
    var baseQuery = GetBaseEntity();

    if (pagingOptions != null)
    {
        baseQuery
            .Skip(pagingOptions.Page.Value * pagingOptions.Limit.Value)
            .Take(pagingOptions.Limit.Value);
    } 

    // I can actually return from this part but I just
    // set it to a variable to see how many rows the query will return
    var query = await baseQuery
        .ProjectTo<EntityDto>(_mappingConfiguration)
        .ToListAsync();

        return query;
}

However, the query still returns the whole set. My assumption is that the GetBaseEntity() just set-up the query but the execution applies on the LoadResourcesMethod where I applied the ToListAsync() in the LoadResources method.

I have tried the following but it didn't work:

1) not chaining the query (IQueryable with Entity Framework - order, where, skip and take have no effect)

if (pagingOptions != null) {
    baseQuery.Skip(pagingOptions.Page.Value * pagingOptions.Limit.Value);
    baseQuery.Take(pagingOptions.Limit.Value);
}

2) adding an "OrderBy" (WEB API IQueryable skip() take())

    var query = await baseQuery
    .OrderBy(x => x.Id)
    .ProjectTo<EntityDto>(_mappingConfiguration)
    .ToListAsync();

    return query;

Any help on building a dynamic pagination from an IQueryable source?

Hanlon answered 4/6, 2019 at 16:11 Comment(1)
Calling Skip and Take don't do anything except return the modified query, which you aren't capturing anywhere.Administer
E
13

You are not updating your query in case of pagination. Skip and Take return a new IEnumerable as a result, instead of modifying the existing one in place. So, you should replace this line:

baseQuery
   .Skip(pagingOptions.Page.Value * pagingOptions.Limit.Value)
   .Take(pagingOptions.Limit.Value);

with:

baseQuery = baseQuery
   .Skip(pagingOptions.Page.Value * pagingOptions.Limit.Value)
   .Take(pagingOptions.Limit.Value);

This way, you assign the new query value to the baseQuery and then when you enumerate it, it will return the expected entities.

Elevation answered 4/6, 2019 at 16:22 Comment(1)
Oh, I have thought chaining would be enough to update the base query by reference. Your solution worked and thanks for it!Hanlon
D
3

Sometimes it won't work if you misordered methods invocation

Wrong:

.Take(1) <-
.Skip(1)

Correct:

.Skip(1)
.Take(1) <-
Dissimulate answered 16/3, 2023 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.