系列导航及源代码

需求

查询中有个非常常见的需求就是后端分页,实现的方式也不算复杂,所以我们本文仅仅演示一个后端查询分页的例子。

目标

实现分页查询返回。

原理与思路

对于分页查询而言,我们需要在请求中获取当前请求的是第几页,每页请求多少项数据。在返回值中需要告诉前端,当前这一页的所有数据项列表,总共的数据项有多少。为此我们可以定义一个包装类型,供系统中所有需要提供后端分页查询返回值使用。

除了最基本的实现方式之外,我们可能还需要实现关于分页数据结构的AutoMapper转换映射,避免手动重复实现。

实现

定义分页结果数据结构

我们在Application/Common/Models中定义一个类,表示分页结果。

  • PaginatedList.cs
  1. using Microsoft.EntityFrameworkCore;
  2. namespace TodoList.Application.Common.Models;
  3. public class PaginatedList<T>
  4. {
  5. public List<T> Items { get; }
  6. public int PageNumber { get; }
  7. public int TotalPages { get; }
  8. public int TotalCount { get; }
  9. public PaginatedList(List<T> items, int count, int pageNumber, int pageSize)
  10. {
  11. PageNumber = pageNumber;
  12. TotalPages = (int)Math.Ceiling(count / (double)pageSize);
  13. TotalCount = count;
  14. Items = items;
  15. }
  16. // 增加属性表示是否有前一页
  17. public bool HasPreviousPage => PageNumber > 1;
  18. // 增加属性表示是否有后一页
  19. public bool HasNextPage => PageNumber < TotalPages;
  20. // 分页结果构建辅助方法
  21. public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
  22. {
  23. var count = await source.CountAsync();
  24. // 注意我们给的请求中pageNumber是从1开始的
  25. var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
  26. return new PaginatedList<T>(items, count, pageNumber, pageSize);
  27. }
  28. }

添加对于分页结果的Mapping Profile

Application/Common/Mappings中新增一个类用于实现关于分页结果的扩展方法:

  • MappingExtensions.cs
  1. using AutoMapper;
  2. using AutoMapper.QueryableExtensions;
  3. using Microsoft.EntityFrameworkCore;
  4. using TodoList.Application.Common.Models;
  5. namespace TodoList.Application.Common.Mappings;
  6. public static class MappingExtensions
  7. {
  8. public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDestination>(this IQueryable<TDestination> queryable, int pageNumber, int pageSize)
  9. {
  10. return PaginatedList<TDestination>.CreateAsync(queryable, pageNumber, pageSize);
  11. }
  12. public static Task<List<TDestination>> ProjectToListAsync<TDestination>(this IQueryable queryable, IConfigurationProvider configuration)
  13. {
  14. return queryable.ProjectTo<TDestination>(configuration).ToListAsync();
  15. }
  16. }

创建分页查询请求

为了演示分页查询的应用,我们新增一个允许分页查询TodoItemQuery

  • GetTodoItemsWithPaginationQuery.cs
  1. using System.Linq;
  2. using AutoMapper;
  3. using AutoMapper.QueryableExtensions;
  4. using MediatR;
  5. using TodoList.Application.Common.Interfaces;
  6. using TodoList.Application.Common.Mappings;
  7. using TodoList.Application.Common.Models;
  8. using TodoList.Application.TodoItems.Specs;
  9. using TodoList.Domain.Entities;
  10. namespace TodoList.Application.TodoItems.Queries.GetTodoItems;
  11. public class GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<TodoItemDto>>
  12. {
  13. public Guid ListId { get; set; }
  14. public int PageNumber { get; set; } = 1;
  15. public int PageSize { get; set; } = 10;
  16. }
  17. public class GetTodoItemsWithPaginationQueryHandler : IRequestHandler<GetTodoItemsWithPaginationQuery, PaginatedList<TodoItemDto>>
  18. {
  19. private readonly IRepository<TodoItem> _repository;
  20. private readonly IMapper _mapper;
  21. public GetTodoItemsWithPaginationQueryHandler(IRepository<TodoItem> repository, IMapper mapper)
  22. {
  23. _repository = repository;
  24. _mapper = mapper;
  25. }
  26. public async Task<PaginatedList<TodoItemDto>> Handle(GetTodoItemsWithPaginationQuery request, CancellationToken cancellationToken)
  27. {
  28. return await _repository
  29. .GetAsQueryable(x => x.ListId == request.ListId)
  30. .OrderBy(x => x.Title)
  31. .ProjectTo<TodoItemDto>(_mapper.ConfigurationProvider)
  32. .PaginatedListAsync(request.PageNumber, request.PageSize);
  33. }
  34. }

创建查询Controller

  • TodoItemController.cs
  1. // 对于查询来说,一般参数是来自查询字符串的,所以这里用[FromQuery]
  2. [HttpGet]
  3. public async Task<ApiResponse<PaginatedList<TodoItemDto>>> GetTodoItemsWithPagination([FromQuery] GetTodoItemsWithPaginationQuery query)
  4. {
  5. return ApiResponse<PaginatedList<TodoItemDto>>.Success(await _mediator.Send(query));
  6. }

验证

启动Api项目,执行创建TodoList的请求:

  • 请求

  • 响应

总结

对于后端排序的需求来说,实现起来并不复杂,但是在这个分页的过程中,要注意一定要以某个不会轻易变动的字段来作为排序的键,否则会在多次请求后续页的过程中出现因为字段变动导致排序结果变动进而引发分页结果的前后不一致的情况。

下一篇文章我们来演示如何实现查询过滤的需求。

使用.NET 6开发TodoList应用(13)——实现查询分页的更多相关文章

  1. 使用.NET 6开发TodoList应用(14)——实现查询过滤

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在查询请求中,还有一类常见的场景是过滤查询,也就是有限制条件的查询,落在数据库层面就是常用的Where查询子句.实现起来也很简 ...

  2. 使用.NET 6开发TodoList应用(15)——实现查询搜索

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 本文我们继续来看查询过程中的另外一个需求:搜索.搜索的含义是目标字段的全部或者部分值匹配请求中的搜索条件,对应到数据库层面是C ...

  3. 使用.NET 6开发TodoList应用(16)——实现查询排序

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 关于查询的另一个需求是要根据前端请求的排序字段进行对结果相应的排序. 目标 实现根据排序要求返回排序后的结果 原理与思路 要实 ...

  4. 使用.NET 6开发TodoList应用(3)——引入第三方日志库

    需求 在我们项目开发的过程中,使用.NET 6自带的日志系统有时是不能满足实际需求的,比如有的时候我们需要将日志输出到第三方平台上,最典型的应用就是在各种云平台上,为了集中管理日志和查询日志,通常会选 ...

  5. 使用.NET 6开发TodoList应用(1)——系列背景

    前言 想到要写这样一个系列博客,初衷有两个:一是希望通过一个实践项目,将.NET 6 WebAPI开发的基础知识串联起来,帮助那些想要入门.NET 6服务端开发的朋友们快速上手,对使用.NET 6开发 ...

  6. 使用.NET 6开发TodoList应用(2)——项目结构搭建

    为了不影响阅读的体验,我把系列导航放到文章最后了,有需要的小伙伴可以直接通过导航跳转到对应的文章 : P TodoList需求简介 首先明确一下我们即将开发的这个TodoList应用都需要完成什么功能 ...

  7. 使用.NET 6开发TodoList应用(4)——引入数据存储

    需求 作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件.对我们的TodoList项目来说,自然也需要配置数据存储.目前的需求很简单: 需要能持久化TodoList对象并对其 ...

  8. 使用.NET 6开发TodoList应用(5)——领域实体创建

    需求 上一篇文章中我们完成了数据存储服务的接入,从这一篇开始将正式进入业务逻辑部分的开发. 首先要定义和解决的问题是,根据TodoList项目的需求,我们应该设计怎样的数据实体,如何去进行操作? 长文 ...

  9. 使用.NET 6开发TodoList应用(5.1)——实现Repository模式

    需求 经常写CRUD程序的小伙伴们可能都经历过定义很多Repository接口,分别做对应的实现,依赖注入并使用的场景.有的时候会发现,很多分散的XXXXRepository的逻辑都是基本一致的,于是 ...

随机推荐

  1. 零基础学习java------26--------获取省访问量的top3,APP版本数据分析,事务,json,json字符串与对象间的相互转换,求电影平均分

    一. day23中的ip,url案例(前面答案错了) 思路分析: 1.创建javabean,用来存储ip.txt各字段的信息 2. 创建java工具类,封装相应的方法 (1) 加载读取ip.txt文档 ...

  2. highchars操作集合

    一.tooltip 与鼠标指针的距离想调整tooltip和鼠标指针的距离,官方api 和中文api中都没写,只有轴 label.distance . 但我觉得应该有这个,看源码果然有 tooltip ...

  3. 【Java 与数据库】How to Timeout JDBC Queries

    How to Timeout JDBC Queries JDBC queries by default do not have any timeout, which means that a quer ...

  4. 网络通信引擎ICE的使用

    ICE是一种网络通信引擎,在javaWeb的开发中可以用于解决局域网内部服务器端与客户端之间的网络通信问题.即可以在 1.在服务器和客户端都安装好ICE 2.服务器端(java)在java项目中引入I ...

  5. 二级C复习

    二级C语言 队列 计算队列中元素个数 种 : rear > front ,直接减 第二种: rear < front 上面两种综合一起,求元素个数公式 :(r - f + maxsize) ...

  6. Mysql资料 xtrabackup

    目录 一.简介 原理 优缺点 二.安装 三.日常使用 备份所有库 增量备份 远程备份 四.参数 一.简介 原理 其实XtraBackup也是基于INNODB的 crash-recovery功能来实现的 ...

  7. vue-cli4结合element-ui异常解决(前端小白,文摘取自网络)

    1:将vue-cli4版本退回到vue-cli3 2:使用element-plus 替换 element-ui 传送门 => https://element-plus.gitee.io/#/zh ...

  8. CF934A A Compatible Pair 题解

    Content 有两个数列 \(A\) 和 \(B\),\(A\) 数列里面有 \(n\) 个元素,\(B\) 数列里面有 \(m\) 个元素,现在请从 \(A\) 数列中删除一个数,使得 \(A\) ...

  9. CF420A Start Up 题解

    Content 给定一个长度为 \(n\) 的字符串,求这个字符串整个反转过来后是否和原字符串一样. 数据范围:\(1\leqslant n\leqslant 10^5\). Solution 众所周 ...

  10. SpringCloud Alibaba实战(12:引入Dubbo实现RPC调用)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 大家好,我是老三,断更了半年,我又滚回来继续写这个系列了,还有人看吗-- 在前面的章 ...