系列導航及源代碼
- 使用.NET 6開發TodoList應用文章索引
需求
查詢中有個非常常見的需求就是后端分頁,實作的方式也不算復雜,所以我們本文僅僅演示一個后端查詢分頁的例子,
目標
實作分頁查詢回傳,
原理與思路
對于分頁查詢而言,我們需要在請求中獲取當前請求的是第幾頁,每頁請求多少項資料,在回傳值中需要告訴前端,當前這一頁的所有資料項串列,總共的資料項有多少,為此我們可以定義一個包裝型別,供系統中所有需要提供后端分頁查詢回傳值使用,
除了最基本的實作方式之外,我們可能還需要實作關于分頁資料結構的AutoMapper轉換映射,避免手動重復實作,
實作
定義分頁結果資料結構
我們在Application/Common/Models中定義一個類,表示分頁結果,
PaginatedList.cs
using Microsoft.EntityFrameworkCore;
namespace TodoList.Application.Common.Models;
public class PaginatedList<T>
{
public List<T> Items { get; }
public int PageNumber { get; }
public int TotalPages { get; }
public int TotalCount { get; }
public PaginatedList(List<T> items, int count, int pageNumber, int pageSize)
{
PageNumber = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
TotalCount = count;
Items = items;
}
// 增加屬性表示是否有前一頁
public bool HasPreviousPage => PageNumber > 1;
// 增加屬性表示是否有后一頁
public bool HasNextPage => PageNumber < TotalPages;
// 分頁結果構建輔助方法
public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = await source.CountAsync();
// 注意我們給的請求中pageNumber是從1開始的
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageNumber, pageSize);
}
}
添加對于分頁結果的Mapping Profile
在Application/Common/Mappings中新增一個類用于實作關于分頁結果的擴展方法:
MappingExtensions.cs
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using TodoList.Application.Common.Models;
namespace TodoList.Application.Common.Mappings;
public static class MappingExtensions
{
public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDestination>(this IQueryable<TDestination> queryable, int pageNumber, int pageSize)
{
return PaginatedList<TDestination>.CreateAsync(queryable, pageNumber, pageSize);
}
public static Task<List<TDestination>> ProjectToListAsync<TDestination>(this IQueryable queryable, IConfigurationProvider configuration)
{
return queryable.ProjectTo<TDestination>(configuration).ToListAsync();
}
}
創建分頁查詢請求
為了演示分頁查詢的應用,我們新增一個允許分頁查詢TodoItem的Query:
GetTodoItemsWithPaginationQuery.cs
using System.Linq;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using MediatR;
using TodoList.Application.Common.Interfaces;
using TodoList.Application.Common.Mappings;
using TodoList.Application.Common.Models;
using TodoList.Application.TodoItems.Specs;
using TodoList.Domain.Entities;
namespace TodoList.Application.TodoItems.Queries.GetTodoItems;
public class GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<TodoItemDto>>
{
public Guid ListId { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
public class GetTodoItemsWithPaginationQueryHandler : IRequestHandler<GetTodoItemsWithPaginationQuery, PaginatedList<TodoItemDto>>
{
private readonly IRepository<TodoItem> _repository;
private readonly IMapper _mapper;
public GetTodoItemsWithPaginationQueryHandler(IRepository<TodoItem> repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
public async Task<PaginatedList<TodoItemDto>> Handle(GetTodoItemsWithPaginationQuery request, CancellationToken cancellationToken)
{
return await _repository
.GetAsQueryable(x => x.ListId == request.ListId)
.OrderBy(x => x.Title)
.ProjectTo<TodoItemDto>(_mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
}
創建查詢Controller
TodoItemController.cs
// 對于查詢來說,一般引數是來自查詢字串的,所以這里用[FromQuery]
[HttpGet]
public async Task<ApiResponse<PaginatedList<TodoItemDto>>> GetTodoItemsWithPagination([FromQuery] GetTodoItemsWithPaginationQuery query)
{
return ApiResponse<PaginatedList<TodoItemDto>>.Success(await _mediator.Send(query));
}
驗證
啟動Api專案,執行創建TodoList的請求:
-
請求

-
回應

總結
對于后端排序的需求來說,實作起來并不復雜,但是在這個分頁的程序中,要注意一定要以某個不會輕易變動的欄位來作為排序的鍵,否則會在多次請求后續頁的程序中出現因為欄位變動導致排序結果變動進而引發分頁結果的前后不一致的情況,
下一篇文章我們來演示如何實作查詢過濾的需求,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/399554.html
標籤:.NET Core
