public class PaginationHelper<T> : IPagination<T>
{
public int PageNumber { get; set; }
public int PageSize { get; set; }
public IList<T> Items { get; set; }
public IList<T> PaginationItems => AsPagination();
public int TotalItems { get; set; }
public int FirstItem => (PageNumber - 1) * PageSize + 1;
public int LastItem => FirstItem + PageSize - 1;
public int TotalPages
{
get
{
var result = (int)Math.Ceiling((double)TotalItems / PageSize);
return result == 0 ? 1 : result;
}
}
public bool HasPreviousPage => PageNumber > 1;
public bool HasNextPage => PageNumber < TotalPages;
public List<T> AsPagination()
{
var numberToSkip = (PageNumber - 1) * PageSize;
var results = Items.Skip(numberToSkip).Take(PageSize).ToList();
return results;
}
}
public class Paginator<T>
{
public List<T> PaginationItems { get; set; }
public int TotalPages { get; set; }
}
My friend gave me this code, and I can't compile. When define a property/method in C#, is it possible to use in this way?
public int FirstItem => (PageNumber - 1) * PageSize + 1; Is it right syntax??
That syntax; formally known as an expression-bodied member was introduced in C# 6 and the VS 2015 C# compiler.
Earlier versions of Visual Studio will not compile it; you'll have to refactor to C# 5 compliant code:
public int FirstItem { get { return (PageNumber - 1) * PageSize + 1; } }
Related
I have a LINQ query that gets the results from a database with filtering and pagination and that maps the result to the PaginatedList<T>.
var bookReservationList = await _dbContext.BookReservations
.Include(x => x.Books).Select(
book => new BookRequestViewModel
{
Author = book.Books.Author
}).PaginatedListAsync(pageNumber, pageSize);
Now with the result obtained from the bookReservationList in the above LINQ query, I wanted to call the GetUserEmailAsync to get the
Requester = await _identityService.GetUserEmailAsync(book.RequesterId), which is the call to UserManager<T>
I have been trying in this way but I am failing: to map the result to Task<PaginatedList>
var res = bookReservationList.Items.Select(async y => new BookRequestViewModel
{
Requester = await _identityService.GetUserEmailAsync(y.RequesterId);
});
PaginatedListAsync
public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDestination>(this IQueryable<TDestination> queryable, int pageNumber, int pageSize)
{
return PaginatedList<TDestination>.CreateAsync(queryable, pageNumber, pageSize);
}
PaginatedList
public class PaginatedList<T>
{
public List<T> Items { get; }
public int PageIndex { get; }
public int TotalPages { get; }
public int TotalCount { get; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
TotalCount = count;
Items = items;
}
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
The implementation of GetUserEmailAsync is done as:
public interface IIdentityService
{
Task<string> GetUserEmailAsync(string userId);
}
ViewModel
public class BookRequestViewModel
{
public int Id { get; set; }
public string Requester { get; set; }
public string RequesterId { get; set; }
public DateTime RequestDate { get; set; }
}
Basically, I want to return the result as
Task<PaginatedList<BookRequestViewModel>> GetRequestedBookList(int pageNumber, int pageSize); but failing to do so.
You can not do that with LINQ. You have to use foreach
foreach (y in bookReservationList.Items)
{
y.Requester = await _identityService.GetUserEmailAsync(y.RequesterId);
}
I had automapper working but then added in the PagedList and now it does not work. How
do I tell it just to map the RfReport to RfReportModel but keep the PagedList parameters?
AutoMapper.AutoMapperMappingException: 'Error mapping types.'
ArgumentException: PWDRS.Core.Helpers.PagedList`1[PWDRS.Application.Models.RfReportModel] needs to have a constructor with 0 args or only optional args.
public PagedList<RfReportModel> GetRfReportListPaged(RfReportParameters rfReportParameters)
{
PagedList<RfReport> rfReports = _rfReportRepository.GetRfReportListPaged(rfReportParameters);
PagedList<RfReportModel> mapped = _mapper.Map<PagedList<RfReportModel>>(rfReports);
return mapped;
}
public class PagedList<T> : List<T>
{
public int CurrentPage { get; private set; }
public int TotalPages { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public bool HasPrevious => CurrentPage > 1;
public bool HasNext => CurrentPage < TotalPages;
public PagedList(List<T> items, int count, int pageNumber, int pageSize)
{
TotalCount = count;
PageSize = pageSize;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
AddRange(items);
}
public static PagedList<T> ToPagedList(IEnumerable<T> source, int pageNumber, int pageSize)
{
var count = source.Count();
var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
EDIT 1: I could not figure out how to do it in automapper but I realized I could recreate the PagedList easily like this to effectively do the same thing.
public PagedList<RfReportModel> GetRfReportListPaged(RfReportParameters rfReportParameters)
{
PagedList<RfReport> rfReports = _rfReportRepository.GetRfReportListPaged(rfReportParameters);
IEnumerable<RfReportModel> mapped = _mapper.Map<IEnumerable<RfReportModel>>(rfReports);
PagedList<RfReportModel> plMapped = PagedList<RfReportModel>.ToPagedList(
mapped,
rfReportParameters.PageNumber,
rfReportParameters.PageSize);
return plMapped;
}
My questions is related to a specific scenario that I don't know how to handle it.
I have a mapper registered between Application and ApplicationModel and vice verse.
Now I am calling a method that returns an IPagedList
GetApplications Method:
IPagedList<Application> GetApplications()
{
IQueryable items = context.Applications...
return new PagedList<Application>(items, 1, 10);
}
And this is where I get my error because PagedList doesn't have a default constructor when it tries to make the mapping.
IPagedList<Application> applications = GetApplications();
var toRet = Mapper.Map<IPagedList<ApplicationModel>>(applications); //here I get the error
I tried to figure out how this is done with ConstructUsing but honestly I need help to structure the call correctly if that is the correct path
Bellow are the interface and the implementation of IPagedList
Interface:
public interface IPagedList<T> : IList<T>
{
int CurrentPage { get; }
int TotalPages { get; }
int PageSize { get; }
int TotalCount { get; }
bool HasPrevious { get; }
bool HasNext { get; }
IEnumerable<T> Items { get; }
}
Implementation:
public class PagedList<T> : List<T>, IPagedList<T>
{
public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
{
Items = items;
TotalCount = count;
PageSize = pageSize;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
AddRange(Items);
}
public PagedList(IQueryable<T> source, int pageNumber, int pageSize) : this(source.AsEnumerable(), source.Count(), pageNumber, pageSize)
{
}
public int CurrentPage { get; }
public int TotalPages { get; }
public int PageSize { get; }
public int TotalCount { get; }
public bool HasPrevious => CurrentPage > 1;
public bool HasNext => CurrentPage < TotalPages;
public IEnumerable<T> Items { get; }
}
Use ProjectTo<ApplicationModel>() or map the IQueryable<Applications> to List<ApplicationModels>, then throw it into PagedList's constructor and won't deal with the default constructor issue in AutoMapper.
ProjectTo Example:
var toRet = new PagedList<ApplicationModel>(context.Applications...ProjectTo<ApplicationModel>(), 1, 10);
I have the following paging logic that works very well. If I decide to load 5 items at the time (each time) it works fine. However I have the following scenario: I want to initially load 10 items and then each time user clicks load more I want to load 5 items. How can I do that?
public class Page
{
public Page(int totalItems, int? page, int pageSize = 5)
{
var totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)pageSize);
var currentPage = page != null ? (int)page : 1;
var startPage = currentPage - 5;
var endPage = currentPage + 4;
if (startPage <= 0)
{
endPage -= (startPage - 1);
startPage = 1;
}
if (endPage > totalPages)
{
endPage = totalPages;
if (endPage > 10)
{
startPage = endPage - 9;
}
}
TotalItems = totalItems;
CurrentPage = currentPage;
PageSize = pageSize;
TotalPages = totalPages;
StartPage = startPage;
EndPage = endPage;
StartIndex = (CurrentPage - 1) * PageSize;
EndIndex = Math.Min(StartIndex + PageSize - 1, TotalItems - 1);
}
public int TotalItems { get; private set; }
public int CurrentPage { get; private set; }
public int PageSize { get; private set; }
public int TotalPages { get; private set; }
public int StartPage { get; private set; }
public int EndPage { get; private set; }
public int StartIndex { get; set; }
public int EndIndex { get; set; }
}
You can check if page is zero or 1 depending on the index you started if so this means that it is the first time and return 10 not five, then continue returning 5 as usual since page will be increased and the condition will not apply anymore
I implement in my WCF Services tha pagination throught Data Service but not work, the code from the activation is:
public class Profit : DataService<ProfitEntities>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
// Set page size defaults for the data service.
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.SetEntitySetPageSize("artEntities", 1);
}
}
I use EF 6 + Linq
I retrieve all my Product indeed without a problem but I need the pagination beacuse is a large data.
Thanks you
Code of my Query:
public List<Product> GetAllProduct()
{
ProfitEntities context = new ProfitEntities();
List<Product> product = new List<Product>();
foreach (artEntities art in context.art)
{
product.Add(new Product
{
coArt = art.co_art.Trim(),
desArt = art.art_des.Trim(),
stockAct = Convert.ToDecimal(art.stock_act),
precVta1 = Convert.ToDecimal(art.prec_vta1),
anulado = art.anulado
});
}
if (product.Count > 0)
{
return product;
}
else
throw new Exception("Imposible conseguir lista de Articulos");
}
Thanks you for your help.
I found other way to make the pagination to WCF Services...
I used a two class found in other proyect from MVC and change to my purpose...
Interface IPagedList
public interface IPagedList<T> : IList<T>
{
int PageCount { get; }
int TotalItemCount { get; }
int PageIndex { get; }
int PageNumber { get; }
int PageSize { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
bool IsFirstPage { get; }
bool IsLastPage { get; }
}
Paged Class
public class PagedList<T> : List<T>, IPagedList<T>
{
public PagedList(IEnumerable<T> source, int index, int pageSize, int? totalCount = null)
: this(source.AsQueryable(), index, pageSize, totalCount)
{
}
public PagedList(IQueryable<T> source, int index, int pageSize, int? totalCount = null)
{
if (index < 0)
throw new ArgumentOutOfRangeException("index", "Value can not be below 0.");
if (pageSize < 1)
throw new ArgumentOutOfRangeException("pageSize", "Value can not be less than 1.");
if (source == null)
source = new List<T>().AsQueryable();
var realTotalCount = source.Count();
PageSize = pageSize;
PageIndex = index;
TotalItemCount = totalCount.HasValue ? totalCount.Value : realTotalCount;
PageCount = TotalItemCount > 0 ? (int)Math.Ceiling(TotalItemCount / (double)PageSize) : 0;
HasPreviousPage = (PageIndex > 0);
HasNextPage = (PageIndex < (PageCount - 1));
IsFirstPage = (PageIndex <= 0);
IsLastPage = (PageIndex >= (PageCount - 1));
if (TotalItemCount <= 0)
return;
var realTotalPages = (int)Math.Ceiling(realTotalCount / (double)PageSize);
if (realTotalCount < TotalItemCount && realTotalPages <= PageIndex)
AddRange(source.Skip((realTotalPages - 1) * PageSize).Take(PageSize));
else
AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
#region IPagedList Members
public int PageCount { get; private set; }
public int TotalItemCount { get; private set; }
public int PageIndex { get; private set; }
public int PageNumber { get { return PageIndex + 1; } }
public int PageSize { get; private set; }
public bool HasPreviousPage { get; private set; }
public bool HasNextPage { get; private set; }
public bool IsFirstPage { get; private set; }
public bool IsLastPage { get; private set; }
#endregion
}
My Operation Contract (Class)
private int defaultPageSize = 10;
ProfitEntities context = new ProfitEntities();
public List<Product> GetAllProduct(string value)
{
var artprofit = context.art.Include("colores").Include("lin_art").Include("sub_lin").Include("cat_art").ToList();
int? page = Convert.ToInt32(value);
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
var productListPaged = artprofit.ToPagedList(currentPageIndex, defaultPageSize);
}