.NET Core API 项目中的分页
Posted
技术标签:
【中文标题】.NET Core API 项目中的分页【英文标题】:Pagination in a .NET Core API Project 【发布时间】:2017-09-29 20:46:48 【问题描述】:我正在尝试在 .NET Core
RESTful
API 上实现分页(使用 EF
)。
与其重新发明***,我希望有一种方法可以使用通用函数,该函数可以挂接到 API 处理程序并拦截请求以将它们应用到 EF
查询结果或 .NET Core
中内置的东西我不知道。任何人都可以指出我在.NET Core
中知道的图书馆的大致方向吗?
我以前这样做的方式(并且在非 .NET Core 应用程序中已经完成)是创建一个函数,我必须将参数物理添加到控制器函数(pageSize,pageNumber),这确实很乏味(而且我觉得有点不整洁)将这两个参数添加到每个函数中。
【问题讨论】:
EF 有两个有用的方法:Skip(int)
和 Take(int)
。您可以将两者结合起来仅获取您需要的页面。
据我所知,没有任何内置功能。但我一直这样做的方式是通过一个从存储库返回的类(当对简单应用程序使用存储库模式时),它实现了一个或两个接口(用于排序、分页)。这样 DbContext/IQueryable 接口就不必泄漏到域或应用程序层。使用 CQRS 则完全不同,因为您将拥有针对特定处理程序的命令/查询
【参考方案1】:
没有内置功能远分页,如果你不喜欢它。想象一个控制器方法返回 1.000.000 个结果进行分页,只是从中挑选 10 个。分页由您决定。
繁琐不整洁的控制器方法如
public class FooController : Controller
public IEnumerable<Foo> GetAll(
string Filter,
string Whatever,
...,
int pageNumber = 1,
int pageSize = 20 )
...
可以重组为
public class FooController : Controller
public IEnumerable<Foo> GetAll( GetAllArgs args )
IQueryable<Foo> query = ...
return query.Paginate( args ).ToList();
public class GetAllArgs : QueryArgsBase
public string Filter get; set;
public string Whatever get; set;
public interface IPaginationInfo
int PageNumber get;
int PageSize get;
public abstract class QueryArgsBase : IPaginationInfo
public int PageNumber get; set; = 1;
public int PageSize get; set; = 20;
public static class QueryableExtensions
public static IQueryable<T> Paginate<T>(
this IQueryable<T> source,
IPaginationInfo pagination )
return source
.Skip( ( pagination.PageNumber - 1 ) * pagination.PageSize )
.Take( pagination.PageSize );
更改任何其他控制器方法以具有这样的参数类并继承自 QueryArgsBase
或实现 IPaginationInfo
以使用 QueryableExtensions.Paginate
方法。
【讨论】:
如果我使用从控制器调用的服务层,我应该将分页逻辑放在哪里?【参考方案2】:这是一个基于Sir Rufo's answer的即用型代码:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace ProjectName.Utilities
public static class Extensions
public static async Task<PaginatedResult<T>> paginate<T>(this IQueryable<T> source,
int pageSize, int pageNumber)
return await new PaginatedResult<T>(pageNumber, pageSize).paginate(source);
public class PaginatedResult<T> : ActionResult
private const int defaultPageSize = 20;
private const int maxPageSize = 50;
public int total get; private set;
public int limit get; private set;
public int page get; private set;
public List<T> objects get; private set;
internal PaginatedResult(int pageNumber, int pageSize = defaultPageSize)
limit = pageSize;
page = pageNumber;
if (limit < 0 || limit > maxPageSize)
limit = defaultPageSize;
if (pageNumber < 0)
page = 0;
internal async Task<PaginatedResult<T>> paginate(IQueryable<T> queryable)
total = queryable.Count();
if (limit > total)
limit = total;
page = 0;
int skip = page * limit;
if (skip + limit > total)
skip = total - limit;
page = total / limit - 1;
objects = await queryable.Skip(skip).Take(limit).ToListAsync();
return this;
在您的控制器中:
// ...
[HttpGet]
public async Task<ActionResult<PaginatedResult<MyDataType>>> getMyData(int pageSize = 20,
int pageNumber = 0)
return await _context.myData.AsNoTracking().paginate(pageSize, pageNumber);
// ...
【讨论】:
以上是关于.NET Core API 项目中的分页的主要内容,如果未能解决你的问题,请参考以下文章