Open jakoss opened 5 years ago
We had the same requirement and just created an extension method:
public static class ISieveProcessorExtensions
{
public static async Task<PagedResult<T>> GetPagedAsync<T>(this ISieveProcessor sieveProcessor, IQueryable<T> query, SieveModel sieveModel = null) where T : class
{
var result = new PagedResult<T>();
var (pagedQuery, page, pageSize, rowCount, pageCount) = await GetPagedResultAsync(sieveProcessor, query, sieveModel);
result.CurrentPage = page;
result.PageSize = pageSize;
result.RowCount = rowCount;
result.PageCount = pageCount;
result.Results = await pagedQuery.ToListAsync();
return result;
}
private static async Task<(IQueryable<T> pagedQuery, int page, int pageSize, int rowCount, int pageCount)> GetPagedResultAsync<T>(ISieveProcessor sieveProcessor, IQueryable<T> query, SieveModel sieveModel = null) where T : class
{
var page = sieveModel?.Page ?? 1;
var pageSize = sieveModel?.PageSize ?? 50;
if (sieveModel != null)
{
// apply pagination in a later step
query = sieveProcessor.Apply(sieveModel, query, applyPagination: false);
}
var rowCount = await query.CountAsync();
var pageCount = (int)Math.Ceiling((double)rowCount / pageSize);
var skip = (page - 1) * pageSize;
var pagedQuery = query.Skip(skip).Take(pageSize);
return (pagedQuery, page, pageSize, rowCount, pageCount);
}
}
public class PagedResult<T> where T : class
{
public IList<T> Results { get; set; }
public int CurrentPage { get; set; }
public int PageCount { get; set; }
public int PageSize { get; set; }
public long RowCount { get; set; }
public PagedResult()
{
Results = new List<T>();
}
}
Hope this helps :)
We did paging ourselves. I just wanted to make some pr here 😁
@skolmer Thanks - this is helpful 😄
I like the idea as I had to do the same for my projects. Added it to plan for v3.0
Did something similar to the example above.
public class PagedResult<T>
{
public PagedResult(int page, int pageCount, int pageSize, long rowCount, IQueryable<T> results)
{
Page = page;
PageCount = pageCount;
PageSize = pageSize;
RowCount = rowCount;
Results = results;
}
public int Page { get; }
public int PageCount { get; }
public int PageSize { get; }
public long RowCount { get; }
public IQueryable<T> Results { get; }
}
public interface IDataSieveProcessor : ISieveProcessor
{
public Task<PagedResult<T>> ApplyAsync<T>(
SieveModel sieveModel,
IQueryable<T> source,
object[]? dataForCustomMethods = null,
bool applyFiltering = true,
bool applySorting = true
);
}
public class DataSieveProcessor : SieveProcessor, IDataSieveProcessor
{
private readonly IOptions<SieveOptions> _options;
public DataSieveProcessor(IOptions<SieveOptions> options) : base(options)
{
_options = options;
}
protected override SievePropertyMapper MapProperties(SievePropertyMapper mapper)
{
mapper.Property<Person>(person => person.FirstName)
.CanFilter()
.CanSort();
mapper.Property<Person>(person => person.LastName)
.CanFilter()
.CanSort();
return mapper;
}
public async Task<PagedResult<T>> ApplyAsync<T>(
SieveModel sieveModel,
IQueryable<T> source,
object[]? dataForCustomMethods = null,
bool applyFiltering = true,
bool applySorting = true
)
{
var result = Apply(sieveModel, source, dataForCustomMethods, applyFiltering, applySorting,
false);
var page = sieveModel.Page ?? 1;
var pageSize = sieveModel.PageSize ?? _options.Value.DefaultPageSize;
var rowCount = await result.LongCountAsync();
var pageCount = (int)Math.Ceiling((double)rowCount / pageSize);
result = Apply(sieveModel, result, dataForCustomMethods, false, false);
return new PagedResult<T>(page, pageCount, pageSize, rowCount, result);
}
}
Doing this in Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.Configure<SieveOptions>(Configuration.GetSection("Sieve"));
services.AddScoped<IDataSieveProcessor, DataSieveProcessor>();
}
I can probably find some time to contribute, make it more generic and part of the library via pull request, if there's any interest.
I wanted to keep IQueryable in the PagedResult so I can do custom mapping afterwards, to another class (rest api representation for example).
Hi,
Have you considered adding some kind of paging info on
Apply
result? It's common pattern that we need Page, PageSize and TotalCount of filtered items. The only option now is to callApply
2 times (one with turned off paging) to get TotalCount, but that's highly unefficient since there is no caching of expressions and reflections in library (that might be another case of issue though).I think the api could look like:
I don't really like the out object passed to function. But the alternative is maybe to return object from
Apply
? But that's pretty big breaking change. Please let me know what you think.EDIT: I'm more than happy to create PR with this as soon as we can figure out how API could look like :)