【问题标题】:Pagination in a .NET Core API Project.NET Core API 项目中的分页
【发布时间】: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 则完全不同,因为您将拥有针对特定处理程序的命令/查询

标签: c# rest pagination asp.net-core


【解决方案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);
}
// ...

【讨论】:

    猜你喜欢
    • 2017-12-04
    • 1970-01-01
    • 2021-05-22
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 2018-01-06
    • 1970-01-01
    相关资源
    最近更新 更多