How to add a Paging to asp.net razor page - c#

How to create a pagging for grid that looks something like below; it has with 10 page buttons & and in end [...] button
[1][2][3][4][5][6][7][8][9][10][...]
[...] button will load next 10 pages (assuming there are next 10 pages). if there are only 2 more pages. than it will load next 2 pages. and [...] dispears. below is an example of that
[3][4][5][6][7][8][9][10][11][12]
below are some more examples
[1][2][3]
[11][12][13][14][15][16][17][18][19][20][...]
Index.csHtml - front-end page - here I am creating a for loop to load 10 paging buttons. I am using PageIndex to pass URL variable to tell which page I am on. Here paging work fine for 10 buttons. but I am not sure how to create [...] button that will load next 10 pages
#{
var StartPage = 1;
var LastPage = 10;
var IncPage = 10;
for (int x = StartPage; x <= LastPage; x++)
{
<a asp-page="./Index"
asp-route-pageIndex="#x"
class="btn btn-primary">#x</a>
}
}
Index.csHmtl - back-end file. here I am creating a List by using paginatedList class.
public PaginatedList<CourseTaken_Model> CourseTakenList { get; set; } = default!;
CourseTakenList = await PaginatedList<CourseTaken_Model>.CreateAsync(CourseTakenList_Temp.AsNoTracking(), pageIndex ?? 1, pageSize);
PaginatedList class - this is basic class taken from Microsoft Website documents. Here there is PagexIndex(current page), TotalPages, HasPreviousPage & HasNextPage;
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
AddRange(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);
}
}

Related

Mapster not mapping members of generic class inherited from generic list

I'm having difficulties getting all members mapped in the following scenario:
I have a class that inherits from List<T>:
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 PagedList()
{
//default constructor added because Mapster complained about missing default constructor
}
public static async Task<PagedList<T>> ToPagedListAsync(IQueryable<T> source, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
{
var count = source.Count();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(cancellationToken);
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
Implementation:
public async Task<PagedList<UserDTO>> GetAllAsync(UserParameters userParameters, CancellationToken cancellationToken = default)
{
PagedList<User> users = await _repositoryManager.UserRepository.GetAllAsync(userParameters, cancellationToken);
//users.Count = 5
//users.TotalPages = 10
TypeAdapterConfig<PagedList<User>, PagedList<UserDTO>>.NewConfig()
.IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal ||
member.AccessModifier == AccessModifier.ProtectedInternal);
PagedList<UserDTO> usersDTO = users.Adapt<PagedList<UserDTO>>();
//usersDTO.Count = 5
//BUT usersDTO.TotalPages = 0 (should be 10)
return usersDTO;
}
In the above scenario, the items of the inherited List<User> list in PagedList<User> are converted properly, whereas the other members, i.e. CurrentPage, TotalPages, PageSize, TotalCount, HasPrevious and HasNext are not.
I tried to configure Mapster to include also hidden members, but to no avail:
TypeAdapterConfig<PagedList<User>, PagedList<UserDTO>>.NewConfig()
.IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal ||
member.AccessModifier == AccessModifier.ProtectedInternal);
How do I go about to make this conversion work?
I was facing similar issue and didn't find any clear way, so I made a workaround by adding a constructor with count, pageNumber and pageSize properties.
Then I configured to use this constructor by calling ConstructUsing.
TypeAdapterConfig<PagedList<User>, PagedList<UserDTO>>.NewConfig()
.ConstructUsing(src =>
new PagedList<UserDTO>(src.TotalCount, src.PageSize, src.CurrentPage));
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) : this(count, pageNumber, pageSize)
{
AddRange(items);
}
public PagedList(int count, int pageNumber, int pageSize)
{
TotalCount = count;
PageSize = pageSize;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
}
public static async Task<PagedList<T>> ToPagedListAsync(IQueryable<T> source, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
{
var count = source.Count();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(cancellationToken);
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
I have finally found a “solution” to this problem by changing my approach.
I changed my service implementation to:
public async Task<(object, List<UserDTO>)> GetAllAsync(UserParameters userParameters, CancellationToken cancellationToken = default)
{
PagedList<User> users = await _repositoryManager.UserRepository.GetAllAsync(userParameters, cancellationToken);
var metadata = new
{
users.TotalCount,
users.PageSize,
users.CurrentPage,
users.TotalPages,
users.HasNext,
users.HasPrevious
};
var usersDTO = users.Adapt<List<UserDTO>>();
var retVal = (metadata, usersDTO);
return retVal;
}
Then in the receiving controller method, I serialise metadata and adds it to the header and returning the list.
Like this:
public async Task<IActionResult> GetUsers([FromQuery] UserParameters userParameters, CancellationToken cancellationToken)
{
var tuple = await _serviceManager.UserService.GetAllAsync(userParameters, cancellationToken);
var metadata = tuple.Item1;
var usersDTO = tuple.Item2;
Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(metadata));
var links = _userLinks.TryGenerateLinks(usersDTO, userParameters.Fields, HttpContext);
return links.HasLinks ? Ok(links.LinkedEntities) : Ok(links.ShapedEntities);
}
Thanks to Marinko Spasojevic at Code-Maze for pointing me in the right direction.

Generic class not returning value all the properties to calling method

I want to return the current pages and total pages form a generic method.
The implementation of the generic function is done as:
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; set; }
public int TotalPages { get; set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage
{
get
{
return (PageIndex > 1);
}
}
public bool HasNextPage
{
get
{
return (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);
}
}
However, this function does not return the PageIndex and TotalPages to the calling function.
It always returns the items list.
I am calling the method as:
public async Task<IActionResult> UserActivityList(string currentFilter,
string searchString, int? pageIndex, int pageSize = 10)
{
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
string CurrentFilter = searchString;
IQueryable<UserActivityList> results = _logContext.UserActivityLists;
if (!string.IsNullOrEmpty(searchString))
{
results = results.Where(s => s.Name.Contains(searchString));
}
var UserActivityLists = await PaginatedList<UserActivityList>.CreateAsync(results.AsNoTracking(), pageIndex ?? 1, pageSize);
return Ok(UserActivityLists);
}
The value of UserActivityList, I have formatted the output to JSON, while am calling through a PostMan.
[
{
"id": 2186,
"name": "abc cbc"
},
{
"id": 2152,
"name": "def efg"
},
{
"id": 2238,
"name": "kgh tbf"
},
{
"id": 2172,
"name": "loc tbh"
},
{
"id": 1651,
"name": "abc abc"
}
]
What is the reason the PaginatedList not returning other property? What is the thing I am missing here?
This is due to the serializer just seeing an IEnumerable<T> and doing it's (default) thing. Can I suggest that your class does not inherit from List<T> as it should not have an is-a relationship - it should be a has-a relationship which means your PaginatedList should expose a List<T> (or IEnumerable<T>) property.
With this change you can more exactly control how serialization of your object is handled.
public class PaginatedList<T>
{
public int PageIndex { get; set; }
public int TotalPages { get; set; }
public List<T> Items { get; set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
Items = items;
}
public bool HasPreviousPage
{
get
{
return (PageIndex > 1);
}
}
public bool HasNextPage
{
get
{
return (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);
}
}
There is this good question/answer about inheriting from List<T>: Why not inherit from List<T>?
A very simple solution is of course to refactor your code so that you do not inherit form List, but expose a List as a property:
public class Document<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set;}
public List<T> Pages {get; private set; }
public Document(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.Pages = new List<T>(items);
}
// ...
}

Entity Framework running SQL query with desired output

When using Linq I am not able to make use of SQL Full-Text Search capability so I would like to use SqlQuery method on DbSet which allows me to write a raw SQL query.
Let consider following code:
IQueryable<Message> query = DBContext.Set<Message>();
PagedList<Message> messages = query.Where(x => x.Content.Contains("magic"))
.OrderBy(x => x.CreatedDate)
.ToPagedList<Message>(50, 2);
if I would like to run following SqlQuery query:
DBContext.Set<Message>().SqlQuery("SELECT * FROM Message WHERE CONTAINS("Content", 'magic')");
SqlQuery method returns IEnumerable<Message> and I would like to have PagedList<Message> object returned. What would be the optimal(good) way to achieve it? Notice that I would like my database to execute SQL query and not to filter in memory.
PagedList implementation
public class PagedList<T> : List<T>, IPagedList
{
public PagedList()
{
this.TotalCount = 0;
this.PageSize = 0;
this.PageIndex = 0;
this.TotalPages = 0;
this.CurrentPage = 0;
}
public PagedList(IQueryable<T> source, int pageIndex, int pageSize)
{
this.TotalCount = source.Count();
this.PageSize = pageSize;
this.PageIndex = pageIndex;
this.TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
this.CurrentPage = Math.Max(1, pageIndex);
this.AddRange(source.Skip((this.CurrentPage - 1) * pageSize).Take(pageSize).ToList());
}
public PagedList(List<T> source, int index, int pageSize)
{
this.TotalCount = source.Count();
this.PageSize = pageSize;
this.PageIndex = index;
this.TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
this.CurrentPage = Math.Max(1, index);
this.AddRange(source.Skip(index * pageSize).Take(pageSize).ToList());
}
public int TotalCount { get; set; }
public int TotalPages { get; set; }
public int PageSize { get; set; }
public int PageIndex { get; set; }
public int CurrentPage { get; set; }
}
Paged list extension method:
public static PagedList<T> ToPagedList<T>(this IQueryable<T> query, int pageIndex, int pageSize) where T : class
{
return new PagedList<T>(query, pageIndex, pageSize);
}
Have you tried to simply do
DBContext.Set<Message>().SqlQuery("SELECT * FROM Message WHERE CONTAINS("Content", 'magic')")
.AsQueryable()
.ToPagedList<Message>(50, 2);

How to make paging start from 1 instead of 0

I used the paging example of the Nerddinner tutorial. But I also wanted to add page Numbers, somehting like that:
<<< 1 2 3 4 5 6 >>>
The code below works if i start my paging from 0, but not from 1. How can I fix this ?
Here is my code:
PaginatedList.cs
public class PaginatedList<T> : List<T> {
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = source.Count();
TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 0);
}
}
public bool HasNextPage {
get {
return (PageIndex+1 < TotalPages);
}
}
}
UserController.cs
public ActionResult List(int? page)
{
const int pageSize = 20;
IUserRepository userRepository = new UserRepository();
IQueryable<User> listUsers = userRepository.GetAll();
PaginatedList<User> paginatedUsers = new PaginatedList<User>(listUsers, page ?? 0, pageSize);
return View(paginatedUsers);
}
List.cshtml
#if (Model.HasPreviousPage)
{
#Html.RouteLink(" Previous ", "PaginatedUsers", new { page = (Model.PageIndex - 1) })
}
#for (int i = 1; i <= Model.TotalPages; i++)
{
#Html.RouteLink(#i.ToString(), "PaginatedUsers", new { page = (#i ) })
}
#if (Model.HasNextPage)
{
#Html.RouteLink(" Next ", "PaginatedUsers", new { page = (Model.PageIndex + 1) })
}
PaginatedList.cs
.Skip((PageIndex -1) * PageSize).Take(PageSize)
UserController.cs
public ActionResult List(int page = 1)
{
I have made this changes :
PaginatedList.cs
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
.
.
.
this.AddRange(source.Skip((PageIndex -1) * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 1);
}
}
public bool HasNextPage {
get {
return (PageIndex < TotalPages);
}
}
UserController.cs
public ActionResult List(int? page)
{
const int pageSize = 20;
page = (page < 1) ? 1 : page ?? 0;
.
.
.
I would suggest to use a library instead of going to write your own pagination code. MvcPaging is one of those library.
See my answer here - what good libraries to use for paging on custom html (not table based)?

MongoDB c# : Question about pagination

Using a paged result of some query i need to get from what page is a point.The object is return the data positioned at the right page when you push the point out of the scope opening the paged result at this page.
If the paged result can be obtain like this sample , how i can get from an item from what page is comming ?
paging
.skip(PAGE_SIZE * (PAGE_NUMBER - 1)).limit(PAGE_SIZE)
public List<BsonItem> GetData(QueryComplete query, int take, int skip, SortByBuilder sort)
{
var cursor = Db.Data.FindAs<BsonItem>(query);
if (skip > 0)
cursor.SetSkip(skip);
if (take > 0)
cursor.SetLimit(take);
if (sort != null )
cursor.SetSortOrder(sort);
return cursor.ToList();
}
Thanks for your help.
Page number should( and possible sort order and direction) come from the client side. So client click on the some page and than..
Personally i using some kind of filter that contains all properties that you need.
Using that filter you just need to specify page that you need to display in grid and how much items you need per page.
var pageNumber = 1;// current page should come from the client
var filter = new BaseFilter(){CurrentPage = pageNumber, ItemsPerPage = 30};
var items = GetItemsByFilter(filter, Query.LTE("SomeDate",DateTime.Now)),
SortBy.Ascending("SortField"));
//For basic paging you only following three properties
var totalCount = filter.TotalCount; // here will be total items count
var pagesCount = filter.TotalPagesCount; // here will be total pages count
// pageNumber = current page
Also you can inferit from BasicFilter and add any properties that you need for quering, sorting.
Here filter code(hope it will be useful for you):
public List<Item> GetItemsByFilter(BaseFilter filter,
QueryComplete query, SortByBuilder sort)
{
var resultItems = new List<Item>();
var cursor = Db.Data.FindAs<BsonItem>(query);
cursor.SetSortOrder(sort);
if (filter.IsNeedPaging)
{
cursor.SetSkip(filter.Skip).SetLimit(filter.Take);
filter.TotalCount = cursor.Count();
}
resultItems.AddRange(cursor);
return resultItems;
}
public class BaseFilter
{
private int _itemsPerPage = 10;
private int _skip = 0;
private int _currentPage = 1;
public BaseFilter()
{
IsNeedPaging = true;
}
public int Skip
{
get
{
if (_skip == 0)
_skip = (CurrentPage - 1) * _itemsPerPage;
return _skip;
}
set
{
_skip = value;
}
}
public int Take
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public bool IsNeedPaging { get; set; }
public int TotalCount { get; set; }
public int CurrentPage
{
get
{
return _currentPage;
}
set
{
_currentPage = value;
}
}
public int ItemsPerPage
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public int TotalPagesCount
{
get
{
return TotalCount / ItemsPerPage +
((TotalCount % ItemsPerPage > 0) ? 1 : 0);
}
}
}

Categories