.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 项目中的分页的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 多页分页

django的分页--不全也未实现

asp.net webforms中Jquery Datatable中的分页

RESTful API 设计中的分页问题

帝国cms——首页怎么分页??

Gitlab API 中的分页仅返回 100 per_page max