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;
}
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've added paging to my headers in DotNetCore 3.0 and I've followed a video tutorial on Udemy to do this, but it's not returning any results in the response. Before I added paging this worked fine and did return the users, but since adding the headers the status response is 200 OK as expected, but no results.
This modifies the headers to return the page size, current page etc (this is where I suspect the problem is as the camelCaseFormatter is not applied either):
public static void AddPagination(this HttpResponse response, int currentPage, int itemsPerPage, int totalItems, int totalPages) {
var paginationHeader = new PaginationHeader(currentPage, itemsPerPage, totalItems, totalPages);
var camelCaseFormatter = new JsonSerializerSettings();
camelCaseFormatter.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
response.Headers.Add("Pagination", JsonConvert.SerializeObject((paginationHeader, camelCaseFormatter)));
response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
}
This is the Users Controller which does the work of actually getting the users from the DB:
[HttpGet]
public async Task<IActionResult> GetUsers([FromQuery]UserParams userParams) {
var users = await _repo.GetUsers(userParams);
var usersToReturn = _mapper.Map<IEnumerable<UserForListDto>>(users);
Response.AddPagination(users.CurrentPage, users.PageSize, users.TotalCount, users.TotalPages);
return Ok(usersToReturn);
}
This is the Repo (so the GetUsers call that is above):
public async Task<PagedList<User>> GetUsers(UserParams userParams)
{
var users = _context.Users.Include(p => p.Photos);
return await PagedList<User>.CreateAsync(users, userParams.PageSize, userParams.PageSize);
}
The CreateAsync Method and PagedList Class:
public class PagedList<T> : List<T>
{
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
public PagedList(List<T> items, int count, int pageNumber, int pageSize)
{
TotalCount = count;
PageSize = pageNumber;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double) pageSize);
this.AddRange(items);
}
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize) {
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
And Finally the UserParams class used to calculate the pageSize etc.
public class UserParams
{
private const int MaxPageSize = 50;
public int PageNumber { get; set; } = 1;
private int pageSize = 10;
public int PageSize
{
get { return pageSize; }
set { pageSize = (value > MaxPageSize) ? MaxPageSize : value; }
}
}
The result I get back from Postman is a Status 200 OK with an empty JSON object ([]).
I am following the official ASP.NET Core 2.2 paging example over at Microsoft.
Here, I'm adding a PaginatedList.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace BlatantlyCopiedCode
{
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);
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);
}
}
}
and adding this to my EF object Merge
[HttpGet("[action]")]
public async Task<PaginatedList<Merge>> Index(int? pageNumber)
{
var merges = context.Merges;
int pageSize = 20;
return await PaginatedList<Merge>.CreateAsync(merges.AsNoTracking(), pageNumber ?? 1, pageSize);
}
When the result is serialized to JSON in the controller, the properties from PaginatedList<T> aren't serialized, only the List<Merge>. How can I force the properties from PaginatedList<T> to appear in the resulting JSON?
The reason is that it inherits the List<T> which constrains the result to a list.Modify your code to below
public class PaginatedList<T> //: List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private 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);
this.Items = new List<T>();
this.Items.AddRange(items);
}
//...
}
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);
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; } }