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)?
Related
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.
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);
}
// ...
}
Is there a way to implement Generic filter for NHibernate on a repository layer? As per Microsoft API Guidelines, the sort and filters are passed as string API Guidelines. what will be the approach to that using NHibernate to make it generic?
Please have a look at this post.There is a section Paging with NHibernate.
The code snippet below is from this post.
The approach is to implement a class (in the post it was PagedResult<T>) in your case FilteredResult<T>. Having this class you can implementan extension (in post was NHibernatePagedResultExtensions) in this case NHibernateFilteredResultExtensions.
And the last thing left is to apply this extansion exacly the same way.
Hope this will help.
public abstract class PagedResultBase
{
public int CurrentPage { get; set; }
public int PageCount { get; set; }
public int PageSize { get; set; }
public int RowCount { get; set; }
public string LinkTemplate { get; set; }
public int FirstRowOnPage
{
get { return (CurrentPage - 1) * PageSize + 1; }
}
public int LastRowOnPage
{
get { return Math.Min(CurrentPage * PageSize, RowCount); }
}
}
public class PagedResult<T> : PagedResultBase
{
public IList<T> Results { get; set; }
public PagedResult()
{
Results = new List<T>();
}
}
public static class NHibernatePagedResultExtensions
{
public static async Task<PagedResult<T>> GetPagedAsync<T>(this IQueryable<T> query, int page, int pageSize)
{
var result = new PagedResult<T>
{
CurrentPage = page,
PageSize = pageSize,
RowCount = query.Count()
};
var pageCount = (double)result.RowCount / pageSize;
result.PageCount = (int)Math.Ceiling(pageCount);
var skip = (page - 1) * pageSize;
result.Results = await query.Skip(skip).Take(pageSize).ToListAsync();
return result;
}
}
public async Task<IActionResult> Index(int page = 1)
{
var result = await _session.RunInTransaction(async () =>
{
var books = await _session.Books
.Where(b => b.Title.StartsWith("How to"))
.GetPagedAsync(page, pageSize: 25);
return _mapper.Map<PagedResult<BookModel>>(books);
});
return View(result);
}
I have a list of comments formed. The client wants a new page after every 10 comments, so comment 11 will be on page 2, and so on for as many pages as they get. The commets are formed on a .ashx page becuase of an issue I had with the regular .aspx.cs page. Simply put, how do I accomplish this?
Here is the code of the .ashx page:
public void ProcessRequest (HttpContext context)
{
// ****************************************
if (context.Request["postform"] == "1")
{
videomessage myVideoMessage = new videomessage();
myVideoMessage.video_id = context.Request["video_id"];
myVideoMessage.first_name_submitter = context.Request["first_name_submitter"];
myVideoMessage.last_initial_submitter = context.Request["last_initial_submitter"];
myVideoMessage.message = context.Request["message"];
myVideoMessage.status = "0";
myVideoMessage.Save();
}
// ****************************************
// ****************************************
StringBuilder myStringBuilder = new StringBuilder();
// PULL VIDEOMESSAGES FOR VIDEO_ID
videomessage[] myCommentsList = new videomessage().Listing("video_id", context.Request["video_id"], "entry_date" , "DESC");
// FORM COMMENTS IF MORE THAN ONE COMMENT EXISTS
foreach (videomessage tmpMessage in myCommentsList)
{
if (tmpMessage.status == "0" || tmpMessage.status == "1")
{
myStringBuilder.Append("<div class=\"comment_box\">");
myStringBuilder.Append("<p class=\"comment_date\">");
myStringBuilder.Append(Utility.FormatShortDate(tmpMessage.entry_date) + " " + tmpMessage.first_name_submitter + " " + tmpMessage.last_initial_submitter + "." + "</p>");
if (!String.IsNullOrEmpty(tmpMessage.message))
{
myStringBuilder.Append("<p>" + tmpMessage.message + "</p>");
myStringBuilder.Append("</div>");
}
}
}
string return_str = myStringBuilder.ToString();
// IF NO COMMENTS RETURN THIS
if( String.IsNullOrEmpty(return_str) ) return_str = "<p>No comments currently exist for this video.</p>";
// ****************************************
// RETURN STRING
context.Response.ContentType = "text/plain";
context.Response.Write(return_str);
}
I know there will need to be some math involved as well as assigning it to variables, but I am still new to .NET so any help would be appreciated.
Thanks in advance!
using Linq
const int pageSize = 10;
var paginatedComments = comments.Skip((page ?? 0) * pageSize).Take(pageSize).ToList();
(*) page is input from request
Maybe a little outdated, but have a look at the nerddinner tutorial (http://aspnetmvcbook.s3.amazonaws.com/aspnetmvc-nerdinner_v1.pdf), which describes a pagination principle for MVC using a custom PaginatedList class
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);
}
}
}
Alternative solution would be using a jquery approach (see http://www.jquery4u.com/plugins/10-jquery-pagination-plugins), but I have no experience with either of the plugins suggested on this site.
Have a look at this
currentPage=1
NextPageFirstRecord=(currrentpage-1)*noRecordsPerPage
pageCount=totalrecords/noRecordsPerPage
Hope this helps
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);
}
}
}