系列导航及源代码

需求

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

目标

实现分页查询返回。

原理与思路

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

除了最基本的实现方式之外,我们可能还需要实现关于分页数据结构的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();
}
}

创建分页查询请求

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

  • 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)——实现查询分页的更多相关文章

  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、Scala类型检查和类型转换

    目录 Java 1.类型检查 2.类型转换 Scala 1.类型检查 2.类型转换 Java 1.类型检查 使用:变量 instanceof 类型 示例 String name = "zha ...

  2. 【Python】【Basic】MacOS上搭建Python开发环境

    1. Python3 1.1. 下载地址:https://www.python.org/downloads/mac-osx/ 1.1.1. PKG包安装: 没啥可说的,点点点,下一步而已,不用手动配置 ...

  3. Java 总纲

    Java基础篇 Java资源下载 IntelliJ IDEA为类和方法自动添加注释 为什么JAVA对象需要实现序列化? maven ubantu安装maven Java Maven项目搭建 maven ...

  4. maven的lifecycle

    1.maven clean. 清理项目的target目录 2.maven compile 编译项目 3.maven test 编译项目后,再执行Junit测试方法 4.maven package 编译 ...

  5. 13.Vue.js 组件

    组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽 ...

  6. 莫烦python教程学习笔记——数据预处理之normalization

    # View more python learning tutorial on my Youtube and Youku channel!!! # Youtube video tutorial: ht ...

  7. JavaEE期末复习

    期末复习 基础 jsp技术中嵌入java代码,使用的符号 <%%> 掌握jsp技术中引用其他标签库指令标签的书写 掌握jsp技术中request对象setAttribute( ).setC ...

  8. CF31A Worms Evolution 题解

    Content 有一个长度为 \(n\) 的数组 \(a_1,a_2,a_3,...,a_n\),试找出一个三元组 \((i,j,k)\),使得 \(a_i=a_j+a_k\). 数据范围:\(3\l ...

  9. CF1292B Aroma's Search 题解

    Content 给定一个坐标系,已知第一个点的坐标为 \((x_0,y_0)\),第 \(i(i>0)\) 个点的坐标满足这样的两个递推式:\(x_i=a_xx_{i-1}+b_x,y_i=a_ ...

  10. LuoguP6904 [ICPC2015 WF]Amalgamated Artichokes 题解

    Content 已知常数 \(p,a,b,c,d\),我们知道,第 \(k\) 天的股价公式为 \(price_k=p\times(\sin(a\times k+b)+\cos(c\times k+d ...