第 6 章 高级查询和日志

6.1 分页

在 EF Core 中,数据的查询通过集成语言查询(LINQ)实现,它支持强类型,支持对 DbContext 派生类的 DbSet 类型成员进行访问,DbSet 类实现了 IQueryable 和 IEnumerable 接口,LINQ 形式的查询会通过数据库提供程序转换为数据库查询语言,并最终返回实体集合

接下来,在 Library.API 项目中实现分页功能

添加一个 AuthorResourceParameters 类

namespace Library.API.Helpers
{
public class AuthorResourceParameters
{
public const int MaxPageSize = 50;
private int _pageSize = 10; public int PageNumber { get; set; } = 1; public int PageSize
{
get { return _pageSize; }
set
{
_pageSize = (value > MaxPageSize) ? MaxPageSize : value;
}
}
}
}

修改 GetAuthorsAsync 方法实现分页

[HttpGet]
public async Task<ActionResult<List<AuthorDto>>> GetAuthorsAsync([FromQuery] AuthorResourceParameters parameters)
{
var authors = (await RepositoryWrapper.Author.GetAllAsync())
.Skip(parameters.PageSize * (parameters.PageNumber - 1))
.Take(parameters.PageSize)
.OrderBy(author => author.Name);
var authorDtoList = Mapper.Map<IEnumerable<AuthorDto>>(authors); return authorDtoList.ToList();
}

完成之后可以请求 URL: http://localhost:5001/api/author?pageNumber=2&pageSize=3

添加分页元数据

首先创建一个分页列表类

namespace Library.API.Helpers
{
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 totalCount, int pageNumber, int pageSize)
{
TotalCount = totalCount;
CurrentPage = pageNumber;
PageSize = pageSize; TotalPages = (int) Math.Ceiling((double) totalCount / pageSize);
AddRange(items);
}
}
}

在 IAuthorRepository 添加一个方法

Task<PagedList<Author>> GetAllAsync(AuthorResourceParameters parameters);

在 AuthorRepository 添加实现方法

public async Task<PagedList<Author>> GetAllAsync(AuthorResourceParameters parameters)
{
IQueryable<Author> queryableAuthors = DbContext.Set<Author>(); var totalCount = queryableAuthors.Count();
var items = queryableAuthors.Skip((parameters.PageNumber - 1) * parameters.PageSize)
.Take(parameters.PageSize).ToList(); return new PagedList<Author>(items, totalCount, parameters.PageNumber, parameters.PageSize);
}

为了使创建 PagedList 的逻辑具有通用性,可以在 PagedList 类中添加一个静态方法 CreateAsync

public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
var totalCount = source.Count();
var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
var list = new PagedList<T>(items, totalCount, pageNumber, pageSize);
return await Task.FromResult(list);
}

在 GetAllAsync 中使用

public Task<PagedList<Author>> GetAllAsync(AuthorResourceParameters parameters)
{
IQueryable<Author> queryableAuthors = DbContext.Set<Author>(); //var totalCount = queryableAuthors.Count();
//var items = queryableAuthors.Skip((parameters.PageNumber - 1) * parameters.PageSize)
// .Take(parameters.PageSize).ToList(); //return new PagedList<Author>(items, totalCount, parameters.PageNumber, parameters.PageSize); return PagedList<Author>.CreateAsync(queryableAuthors, parameters.PageNumber, parameters.PageSize);
}

接下来,在 AuthorController 中重构 GetAuthorsAsync 代码

[HttpGet(Name = nameof(GetAuthorsAsync))]
public async Task<ActionResult<List<AuthorDto>>> GetAuthorsAsync([FromQuery] AuthorResourceParameters parameters)
{
var pagedList = await RepositoryWrapper.Author.GetAllAsync(parameters);
var paginationMetedata = new
{
totalCount = pagedList.TotalCount,
pageSize = pagedList.PageSize,
currentPage = pagedList.CurrentPage,
totalPages = pagedList.TotalPages,
previousePageLink = pagedList.HasPrevious
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage - 1,
pageSize = pagedList.PageSize
})
: null,
nextPageLink = pagedList.HasNext
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage + 1,
pageSize = pagedList.PageSize
})
: null
}; Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(paginationMetedata)); //var authors = (await RepositoryWrapper.Author.GetAllAsync())
// .Skip(parameters.PageSize * (parameters.PageNumber - 1))
// .Take(parameters.PageSize)
// .OrderBy(author => author.Name);
var authorDtoList = Mapper.Map<IEnumerable<AuthorDto>>(pagedList); return authorDtoList.ToList();
}

以 Get 方法请求 URL 后,服务器不仅返回所请求的资源,并且在响应的消息头中包含了分页元数据,可以通过 previousePageLink,nextPageLink 的 URL 值直接访问上一页以及下一页数据

6.2 过滤和搜索

过滤,是对资源的一个或多个属性与指定的参数值进行匹配并筛选

通过出生地过滤作者,首先在 AuthorResourceParameters 中添加 BirthPlace 属性

public string BirthPlace { get; set; }

然后,修改 AuthorRepository 的 GetAllAsync 方法

if (!string.IsNullOrWhiteSpace(parameters.BirthPlace))
{
queryableAuthors = queryableAuthors.Where(m => m.BirthPlace.ToLower() == parameters.BirthPlace);
}

接着,修改 AuthorController 的 GetAuthorsAsync 方法中生成分页数据的代码,添加过滤信息

previousePageLink = pagedList.HasPrevious
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage - 1,
pageSize = pagedList.PageSize,
birthPlace = parameters.BirthPlace
})
: null,
nextPageLink = pagedList.HasNext
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage + 1,
pageSize = pagedList.PageSize,
birthPlace = parameters.BirthPlace
})
: null

完成之后可以请求 URL: https://localhost:5001/api/authors?birthplace=beijing&pagesize=2

这样可以看到下一页的 URL 中不仅包含分页参数,也包含过滤参数

搜索功能的实现方式与过滤一样

首先在 AuthorResourceParameters 中添加 SearchQuery 属性

public string SearchQuery { get; set; }

然后,修改 AuthorRepository 的 GetAllAsync 方法

if (!string.IsNullOrWhiteSpace(parameters.SearchQuery))
{
queryableAuthors = queryableAuthors.Where(m =>
m.BirthPlace.ToLower().Contains(parameters.SearchQuery.ToLower()) ||
m.Name.ToLower().Contains(parameters.SearchQuery.ToLower()));
}

接着,修改 AuthorController 的 GetAuthorsAsync 方法中生成分页数据的代码,添加过滤信息

previousePageLink = pagedList.HasPrevious
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage - 1,
pageSize = pagedList.PageSize,
birthPlace = parameters.BirthPlace,
searchQuery = parameters.SearchQuery
})
: null,
nextPageLink = pagedList.HasNext
? Url.Link(nameof(GetAuthorsAsync), new
{
pageNumber = pagedList.CurrentPage + 1,
pageSize = pagedList.PageSize,
birthPlace = parameters.BirthPlace,
searchQuery = parameters.SearchQuery
})
: null

完成之后可以请求 URL: https://localhost:5001/api/authors?searchQuery=author&birthplace=beijing&pagesize=2

这样可以看到下一页的 URL 中不仅包含分页参数,也包含过滤参数和查询参数

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

《ASP.ENT Core 与 RESTful API 开发实战》-- (第6章)-- 读书笔记(上)的更多相关文章

  1. 使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术标准<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  2. 4类Storage方案(AS开发实战第四章学习笔记)

    4.1 共享参数SharedPreferences SharedPreferences按照key-value对的方式把数据保存在配置文件中,该配置文件符合XML规范,文件路径是/data/data/应 ...

  3. 菜单Menu(AS开发实战第四章学习笔记)

    4.5 菜单Menu Android的菜单主要分两种,一种是选项菜单OptionMenu,通过按菜单键或点击事件触发,另一种是上下文菜单ContextMenu,通过长按事件触发.页面的布局文件放在re ...

  4. [Android]《Android艺术开发探索》第一章读书笔记

    1. 典型情况下生命周期分析 (1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart方法就会被调用. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下 ...

  5. 温故知新,使用ASP.NET Core创建Web API,永远第一次

    ASP.NET Core简介 ASP.NET Core是一个跨平台的高性能开源框架,用于生成启用云且连接Internet的新式应用. 使用ASP.NET Core,您可以: 生成Web应用和服务.物联 ...

  6. 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践

    本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主.主要NuGet包:无 一.创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点 1 //定义Person类 ...

  7. 零基础ASP.NET Core WebAPI团队协作开发

    零基础ASP.NET Core WebAPI团队协作开发 相信大家对“前后端分离”和“微服务”这两个词应该是耳熟能详了.网上也有很多介绍这方面的文章,写的都很好.我这里提这个是因为接下来我要分享的内容 ...

  8. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

  9. 从 0 使用 SpringBoot MyBatis MySQL Redis Elasticsearch打造企业级 RESTful API 项目实战

    大家好!这是一门付费视频课程.新课优惠价 699 元,折合每小时 9 元左右,需要朋友的联系爱学啊客服 QQ:3469271680:我们每课程是明码标价的,因为如果售价为现在的 2 倍,然后打 5 折 ...

  10. Asp.Net Core 5 REST API - Step by Step

    翻译自 Mohamad Lawand 2021年1月19日的文章 <Asp.Net Core 5 Rest API Step by Step> [1] 在本文中,我们将创建一个简单的 As ...

随机推荐

  1. GPT应用开发:GPT插件开发指南

    欢迎阅读本系列文章!我将带你一起探索如何利用OpenAI API开发GPT应用.无论你是编程新手还是资深开发者,都能在这里获得灵感和收获. 本文,我们将继续展示聊天API中插件的使用方法,让你能够轻松 ...

  2. C#设计模式12——代理模式的写法

    1. 什么是代理模式? 代理模式是一种结构型设计模式,它允许通过代理对象来控制对真实对象的访问,以提供额外的功能或控制访问权限. 2. 代理模式的作用是什么? 代理模式可以为对象提供保护代理.远程代理 ...

  3. php开发中常见的漏洞点(一) 基础sql注入

    前言 本系列为小迪2022的学习笔记,仅用于自我记录. 正文 在一般情况下,一个网站的首页大致如下 在上方存在着各种各样的导航标签.链接.而一般情况下网站的导航会用参数进行索引的编写,比如id.pag ...

  4. df -h与df -i的区别

    一. df命令详解: linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息 -a 全部文件系统列表 -h ...

  5. 概率图模型 · 概率论基础 | 讲的非常好的 KL 散度博客

    知乎博客链接:https://zhuanlan.zhihu.com/p/425693597 感觉讲的非常好,谢谢善良的博主

  6. 13个构建RESTful API的最佳实践

    前言 Facebook.GitHub.Google和其他许多巨头都需要一种方法来服务和消费数据.在今天的开发环境中,RESTful API仍然是服务和消费数据的最佳选择之一. 但你是否考虑过学习行业标 ...

  7. 【MCU】单片机如何检测市电通断?(应用甚广~)

    [来源]https://mp.weixin.qq.com/s/TQKtEbxS8WSo3D1MecdMIw

  8. [转帖]JVM中年轻代里的对象什么情况下进入老年代?以及老年代垃圾回收算法-标记整理算法

    1.躲过15次GC之后进入老年代 系统刚启动时,创建的各种各样的对象,都是分配在年轻代里. 随着慢慢系统跑着跑着,年轻代满了,就会出发Minor GC ,可能1%的少量存活对像转移到空着的Surviv ...

  9. [转帖]k8s(1.28.2)部署ingress-nginx-controller(1.9.0)

    1.部署ingress-nginx-controller 继在三台虚拟机部署k8s后,需要部署ingress-nginx-controller,才能使设置的ingress规则生效. 1.1下载yaml ...

  10. [转帖]JMeter 接口测试快速入门

    https://my.oschina.net/choerodon/blog/5289725   JMeter简介 JMeter 的特性: 对于多种协议的功能测试和性能测试 Web - HTTP, HT ...