使用.NET 6开发TodoList应用(13)——实现查询分页
系列导航及源代码
需求
查询中有个非常常见的需求就是后端分页,实现的方式也不算复杂,所以我们本文仅仅演示一个后端查询分页的例子。
目标
实现分页查询返回。
原理与思路
对于分页查询而言,我们需要在请求中获取当前请求的是第几页,每页请求多少项数据。在返回值中需要告诉前端,当前这一页的所有数据项列表,总共的数据项有多少。为此我们可以定义一个包装类型,供系统中所有需要提供后端分页查询返回值使用。
除了最基本的实现方式之外,我们可能还需要实现关于分页数据结构的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
的请求:
请求
响应
总结
对于后端排序的需求来说,实现起来并不复杂,但是在这个分页的过程中,要注意一定要以某个不会轻易变动的字段来作为排序的键,否则会在多次请求后续页的过程中出现因为字段变动导致排序结果变动进而引发分页结果的前后不一致的情况。
下一篇文章我们来演示如何实现查询过滤的需求。
使用.NET 6开发TodoList应用(13)——实现查询分页的更多相关文章
- 使用.NET 6开发TodoList应用(14)——实现查询过滤
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在查询请求中,还有一类常见的场景是过滤查询,也就是有限制条件的查询,落在数据库层面就是常用的Where查询子句.实现起来也很简 ...
- 使用.NET 6开发TodoList应用(15)——实现查询搜索
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 本文我们继续来看查询过程中的另外一个需求:搜索.搜索的含义是目标字段的全部或者部分值匹配请求中的搜索条件,对应到数据库层面是C ...
- 使用.NET 6开发TodoList应用(16)——实现查询排序
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 关于查询的另一个需求是要根据前端请求的排序字段进行对结果相应的排序. 目标 实现根据排序要求返回排序后的结果 原理与思路 要实 ...
- 使用.NET 6开发TodoList应用(3)——引入第三方日志库
需求 在我们项目开发的过程中,使用.NET 6自带的日志系统有时是不能满足实际需求的,比如有的时候我们需要将日志输出到第三方平台上,最典型的应用就是在各种云平台上,为了集中管理日志和查询日志,通常会选 ...
- 使用.NET 6开发TodoList应用(1)——系列背景
前言 想到要写这样一个系列博客,初衷有两个:一是希望通过一个实践项目,将.NET 6 WebAPI开发的基础知识串联起来,帮助那些想要入门.NET 6服务端开发的朋友们快速上手,对使用.NET 6开发 ...
- 使用.NET 6开发TodoList应用(2)——项目结构搭建
为了不影响阅读的体验,我把系列导航放到文章最后了,有需要的小伙伴可以直接通过导航跳转到对应的文章 : P TodoList需求简介 首先明确一下我们即将开发的这个TodoList应用都需要完成什么功能 ...
- 使用.NET 6开发TodoList应用(4)——引入数据存储
需求 作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件.对我们的TodoList项目来说,自然也需要配置数据存储.目前的需求很简单: 需要能持久化TodoList对象并对其 ...
- 使用.NET 6开发TodoList应用(5)——领域实体创建
需求 上一篇文章中我们完成了数据存储服务的接入,从这一篇开始将正式进入业务逻辑部分的开发. 首先要定义和解决的问题是,根据TodoList项目的需求,我们应该设计怎样的数据实体,如何去进行操作? 长文 ...
- 使用.NET 6开发TodoList应用(5.1)——实现Repository模式
需求 经常写CRUD程序的小伙伴们可能都经历过定义很多Repository接口,分别做对应的实现,依赖注入并使用的场景.有的时候会发现,很多分散的XXXXRepository的逻辑都是基本一致的,于是 ...
随机推荐
- tomcat在eclipse上发布,Perference下的server找不到解决办法
help--->Install New software得到如下所示 下面work with选项的内容与你的eclipse版本有关 我的eclipse版本为eclipse-java-2019-0 ...
- IDEA 超实用使用技巧分享
前言 工欲善其事 必先利其器 最近受部门的邀请,给入职新人统一培训IDEA,发现有很多新人虽然日常开发使用的是IDEA,但是还是很多好用的技巧没有用到,只是用到一些基本的功能,蛮浪费IDEA这个优 ...
- Linux(CentOS)升级gcc版本
本人使用的是CentOS 6.2 64位系统,由于在安装系统的时候并没有勾选安装gcc编译器,因此需要自行安装gcc编译器. 系统信息查看命令: cat /etc/redhat-release 使用y ...
- static JAVA
static 关键字:使用static修饰的变量是类变量,属于该类本身,没有使用static修饰符的成员变量是实例变量,属于该类的实例.由于同一个JVM内只对应一个Class对象,因此同一个JVM内的 ...
- Linux单机安装Zookeeper
一.官网 https://zookeeper.apache.org/ 二.简介 Apache ZooKeeper致力于开发和维护开源服务器,实现高度可靠的分布式协调. ZooKeeper是一种集中式服 ...
- 【Java 8】Stream API
转自 Java 8 Stream Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式.Stream 是Java8中处理集合的关键抽象概念,它可以对集合进 ...
- TCP协议三步挥手与四步挥手
关于TCP协议 TCP(Transmission Control Protocol, 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.与之对应的是UDP(User Datagram ...
- jQuery - 的几种删除方法,还有他们的区别
1.empty() 清空节点,它能清空元素中的所有后代节点,不能删除自己本身这个节点 2.remove() 该节点与该节点所包含的所有后代节点将同时被删除,提供传递一个筛选的表达式,删除指定合集中的元 ...
- 学习 27 门编程语言的长处,提升你的 Python 代码水平
Python猫注:Python 语言诞生 30 年了,如今的发展势头可谓如火如荼,这很大程度上得益于其易学易用的优秀设计,而不可否认的是,Python 从其它语言中偷师了不少.本文作者是一名资深的核心 ...
- 【JAVA今法修真】 第四章 redis特性 击穿雪崩!
感谢这段时间大家的支持,关注我的微信号:南橘ryc ,回复云小霄,就可以获取到最新的福利靓照一张,还等什么,赶快来加入我们吧~ "明日便是决赛了,咋只会用法器没练过法术呢.". 选 ...