使用 ASP.NET Core Razor 页、Web API 和实体框架进行分页和排序

目录

核心类

数据层

The API

Razor页面


如何使用 .NET Core Razor 页、Web API 和实体框架实现分页和排序,以产生良好的性能。

该项目的特点是:

  • 选择页面大小(Page Size)
  • 页面导航
  • 显示记录编号
  • 列的排序

您可以在这里查看此项目的源和最新更新

https://www.youtube.com/embed/FYeTPTMCW-k?feature=oembed

核心类

第一件事是定义用户可以要求应用程序获取什么:

  • 页面大小(Page Size)
  • 页号
  • 要排序的字段
  • 排序方向

代码如下所示:

public class PageSortParam
    {
        public int PageSize { get; set; } = 10;  //default page size
        public int CurrentPage { get; set; } = 1;

        public string SortField { get; set; } = null;
        public SortDirection SortDir { get; set; } 
    }

    public enum SortDirection
    {
        Ascending = 0,   //default as ascending
        Decending
    }

接下来,我们定义应用程序应返回的,如下所示:

  • 记录总数
  • 页数总数
  • 上一页编号 —— 用于用户导航到上一页时
  • 下一页编号 —— 用于导航到下一页
  • 当前页面上的第一个记录编号
  • 当前页面上的最后一个记录编号

代码如下所示:

public class PageSortResult
    {
        public int TotalCount { get; set; } = 0;
        public int TotalPages { get; set; } = 1;
        public int? PreviousPage { get; set; }
        public int? NextPage { get; set; }
        public int FirstRowOnPage { get; set; }
        public int LastRowOnPage { get; set; }
    }

通过定义用户参数和结果,我们创建从List<T>继承的PageList<T>类,以便我们可以将结果存储在List中。类将接受参数并查找结果。

下面的代码显示了GetData()方法中所需的逻辑。从数据库获取记录的行是对ToListAsync()的调用,它将通过调用Skip()跳过不需要的记录,并且仅通过调用Take()获取所需的记录:

public class PageList<T> : List<T>
    {
        public PageSortParam Param { get; }
        public PageSortResult Result { get; }

        public PageList(PageSortParam param)
        {
            Param = param;
            Result = new PageSortResult();
        }

        public async Task GetData(IQueryable<T> query)
        {
            //get the total count
            Result.TotalCount = await query.CountAsync();
            //find the number of pages
            Result.TotalPages = (int)Math.Ceiling(Result.TotalCount / (double)Param.PageSize);
            //find previous and next page number
            if (Param.CurrentPage - 1 > 0)
                Result.PreviousPage = Param.CurrentPage - 1;
            if (Param.CurrentPage + 1 <= Result.TotalPages)
                Result.NextPage = Param.CurrentPage + 1;
            //find first row and last row on the page
            if (Result.TotalCount == 0)  //if no record found
                Result.FirstRowOnPage = Result.LastRowOnPage = 0;
            else
            {
                Result.FirstRowOnPage = (Param.CurrentPage - 1) * Param.PageSize + 1;
                Result.LastRowOnPage = 
                  Math.Min(Param.CurrentPage * Param.PageSize, Result.TotalCount);
            }

            //if has sorting criteria
            if (Param.SortField != null)
                query = query.OrderBy(Param.SortField + 
                 (Param.SortDir == SortDirection.Ascending ? " ascending" : " descending"));

            List<T> list = await query.Skip((Param.CurrentPage - 1) * 
                           Param.PageSize).Take(Param.PageSize).ToListAsync();
            AddRange(list);  //add the list of items
        }
    }

数据层

在数据层项目中定义了Customer

public class Customer
    {
        [Required]
        public int CustomerId { get; set; }

        [Required, StringLength(80)]
        public string FirstName { get; set; }
        [Required, StringLength(80)]
        public string LastName { get; set; }
    }

我们将定义通用的CRUD接口和实现,唯一的区别是该Get()方法将使用PageList<T>类来仅获取所需的记录,从而通过将工作推送到数据库来提高性能。下面是接口:

public interface ICustomerData
    {
        Task<PageList<Customer>> Get(PageSortParam pageSort);
        Task<Customer> GetCustomerById(int customerId);
        Task<Customer> Update(int customerId, Customer customer);
        Task<Customer> Add(Customer customer);
        Task<int> Delete(int customerId);
    }

和实现:

public class SqlCustomerData : ICustomerData
    {
        public StarterDbContext DbContext { get; }

        public SqlCustomerData(StarterDbContext dbContext)
        {
            DbContext = dbContext;
        }

        public async Task<Customer> Add(Customer customer)
        {
            DbContext.Add(customer);
            await DbContext.SaveChangesAsync();
            return customer;
        }

        public async Task<int> Delete(int customerId)
        {
            Customer c = await this.GetCustomerById(customerId);
            if (c != null)
            {
                this.DbContext.Remove(c);
                await DbContext.SaveChangesAsync();
                return customerId;
            }
            return -1;
        }

        public async Task<PageList<Customer>> Get(PageSortParam pageSortParam)
        {
            PageList<Customer> list = new PageList<Customer>(pageSortParam);
            
            await list.GetData(DbContext.Customer);
            return list;
        }

        public async Task<Customer> GetCustomerById(int customerId)
        {
            Customer c = await this.DbContext.Customer.FindAsync(customerId);
            if (c != null)
                return c;
            return null;
        }

        public async Task<Customer> Update(int customerId, Customer customer)
        {
            Customer c = await GetCustomerById(customerId);
            if (c != null)
            {
                c.FirstName = customer.FirstName;
                c.LastName = customer.LastName;
                await DbContext.SaveChangesAsync();
                return c;
            }
            return null;
        }
    }

实体框架的DbContext很简单

public class StarterDbContext : DbContext
    {
        public DbSet<Customer> Customer { get; set; }

        public StarterDbContext(DbContextOptions<StarterDbContext> options)
            : base(options)
        {
        }
    }

The API

Web API 项目中,我们定义将接受PageSortParam为参数的GetCustomers()方法,在数据层中调用该Get()方法,在响应标头的PageSortResult中添加元数据(如记录总数、总页数等),并在响应正文中提供实际记录:

[Route("api/[controller]")]
    [ApiController]
    public class CustomerController : ControllerBase
    {
        public ICustomerData CustomerData { get; }

        public CustomerController(ICustomerData customerData)
        {
            CustomerData = customerData;
        }

        // GET: api/Customer
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Customer>>> 
               GetCustomers([FromQuery] PageSortParam pageSortParam)
        {
            PageList<Customer> list = await this.CustomerData.Get(pageSortParam);
            //return result metadata in the header
            Response.Headers.Add("X-PageSortResult", JsonSerializer.Serialize(list.Result));
            return Ok(list);
        }

        // GET: api/Customer/5
        [HttpGet("{customerId}")]
        public async Task<ActionResult<Customer>> GetCustomer(int customerId)
        {
            return Ok(await this.CustomerData.GetCustomerById(customerId));
        }

        // PUT: api/Customer/5
        [HttpPut("{customerId}")]
        public async Task<ActionResult<Customer>> 
               PutCustomer(int customerId, Customer customer)
        {
            return Ok(await this.CustomerData.Update(customerId, customer));
        }

        // POST: api/Customer
        [HttpPost]
        public async Task<ActionResult<Customer>> PostCustomer(Customer customer)
        {
            return Ok(await this.CustomerData.Add(customer));
        }

        // DELETE: api/Customer/5
        [HttpDelete("{customerId}")]
        public async Task<ActionResult<int>> DeleteCustomer(int customerId)
        {
            return Ok(await this.CustomerData.Delete(customerId));
        }
    }

Razor页面

我们定义用户可以选择的页面大小(page size)

public IEnumerable<SelectListItem> PageSizeList { get; set; } = 
                                   new SelectList(new List<int> { 5, 10, 25, 50 });

我们使用[BindProperty(SupportsGet=true)]将值传递给HTML页面,并获取该值。对于每次访问,我们需要从 HTML 页面传递和获取以下属性:

  • PageSize——用户请求的页面大小(page size)
  • PageNumber——用户当前的页号
  • SortField——用户请求排序的列
  • SortDir——它应该排序的方向
  • SortDirNext——用户单击列链接时的下一个排序方向
[BindProperty(SupportsGet = true)]
        public int? PageSize { get; set; }

        [BindProperty(SupportsGet = true)]
        public int PageNumber { get; set; } = 1;

        [BindProperty(SupportsGet = true)]
        public string SortField { get; set; }  

        [BindProperty(SupportsGet = true)]
        public SortDirection SortDir { get; set; }

        //for the next sort direction when the user clicks on the header
        [BindProperty(SupportsGet = true)]
        public SortDirection? SortDirNext { get; set; }

OnGet()方法将获取每个属性的值,构建参数并将其传递给API,然后显示记录并显示有关页面的元数据。以下是完整的代码:

public class ListModel : PageModel
    {
        public IEnumerable<Dto.Customer> CustomerList { get; set; }
        private readonly IConfiguration config;

        public IEnumerable<SelectListItem> 
           PageSizeList { get; set; } = new SelectList(new List<int> { 5, 10, 25, 50 });

        public PageSortParam PageSortParam { get; set; } = new PageSortParam();
        public PageSortResult PageSortResult { get; set; }

        [BindProperty(SupportsGet = true)]
        public int? PageSize { get; set; }

        [BindProperty(SupportsGet = true)]
        public int PageNumber { get; set; } = 1;

        [BindProperty(SupportsGet = true)]
        public string SortField { get; set; }  

        [BindProperty(SupportsGet = true)]
        public SortDirection SortDir { get; set; }

        //for the next sort direction when the user clicks on the header
        [BindProperty(SupportsGet = true)]
        public SortDirection? SortDirNext { get; set; }

        public ListModel(IConfiguration config)
        {
            this.config = config;
        }

        public async Task OnGet()
        {
            if (PageSize.HasValue)
                PageSortParam.PageSize = (int)PageSize;

            PageSortParam.CurrentPage = PageNumber;

            //if never sorted
            if (SortField == null)
                SortDir = new SortDirection();
            else if (SortDirNext != null)  //if requested new sort direction
                SortDir = (SortDirection)SortDirNext;

            //SortDirNext will be the reverse of SortDir
            SortDirNext = SortDir == SortDirection.Ascending ? 
                          SortDirection.Decending : SortDirection.Ascending;
            
            PageSortParam.SortField = SortField;
            PageSortParam.SortDir = SortDir;
            
            HttpResponseMessage response = await new HttpClient().GetAsync
              (this.config["APIurl"] + "Customer?PageSize=" + PageSortParam.PageSize
                                     + "&CurrentPage=" + PageSortParam.CurrentPage
                                     + "&SortField=" + PageSortParam.SortField
                                     + "&SortDir=" + PageSortParam.SortDir);
            //display the list of customers
            if (response.IsSuccessStatusCode)
                CustomerList = await response.Content.ReadAsAsync<IEnumerable<Dto.Customer>>();
            //get the paging meta data from the header
            IEnumerable<string> headerValue;
            if (response.Headers.TryGetValues("X-PageSortResult", out headerValue))
            {
                PageSortResult = JsonConvert.DeserializeObject<PageSortResult>
                                 (headerValue.First());
            }
        }
    }

html页面将通过使用http get提交表单或单击链接来接受用户的输入。请注意,参数是在每个动作中传递的。在列标题链接中仅指定排序列的名称和方向:

<div>
    <div>
        <table class="table table-bordered table-hover table-sm w-auto">
            <caption>Items @Model.PageSortResult.FirstRowOnPage 
                     to @Model.PageSortResult.LastRowOnPage</caption>
            <thead class="thead-light">
                <tr>
                    <th scope="col">
                        <a asp-page="./Edit" asp-route-customerId="0">
                            <i class="material-icons icon">add_box</i>
                        </a>
                    </th>
                    <th scope="colgroup" colspan="4" class="text-right">
                        <form method="get">
                            Page Size:
                            @Html.DropDownListFor(m => m.PageSize, Model.PageSizeList, 
                                  "-Select-", new { onchange = "submit()" })
                            <input type="hidden" name="PageNumber" value="1" /> 
                            <input type="hidden" name="SortField" value="@Model.SortField" />
                            <input type="hidden" name="SortDir" value="@Model.SortDir" />
                        </form>
                    </th>
                </tr>
                <tr>
                    <th scope="col" class="pl-2 pr-2">
                        <a asp-page="./List"
                           asp-route-SortField="CustomerId"
                           asp-route-SortDir="@Model.SortDir"
                           asp-route-SortDirNext="@Model.SortDirNext"
                           asp-route-PageSize="@Model.PageSize"
                           asp-route-PageNumber="@Model.PageNumber">
                            Customer ID
                        </a>
                    </th>
                    <th scope="col" class="pl-2 pr-2">
                        <a asp-page="./List"
                           asp-route-SortField="FirstName"
                           asp-route-SortDir="@Model.SortDir"
                           asp-route-SortDirNext="@Model.SortDirNext"
                           asp-route-PageSize="@Model.PageSize"
                           asp-route-PageNumber="@Model.PageNumber">
                            First Name
                        </a>
                    </th>
                    <th scope="col" class="pl-2 pr-2">
                        <a asp-page="./List"
                           asp-route-SortField="LastName"
                           asp-route-SortDir="@Model.SortDir"
                           asp-route-SortDirNext="@Model.SortDirNext"
                           asp-route-PageSize="@Model.PageSize"
                           asp-route-PageNumber="@Model.PageNumber">
                            Last Name
                        </a>
                    </th>
                    <th scope="col"></th>
                    <th scope="col"></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var c in Model.CustomerList)
                {
                    <tr>
                        <td class="pl-2 pr-2">@c.CustomerId</td>
                        <td class="pl-2 pr-2">@c.FirstName</td>
                        <td class="pl-2 pr-2">@c.LastName</td>
                        <td class="td-center pl-2 pr-2">
                            <a asp-page="./Edit" asp-route-customerId="@c.CustomerId">
                                <i class="material-icons icon">edit</i>
                            </a>
                        </td>
                        <td class="td-center pl-2 pr-2">
                            <a asp-page="./Delete" asp-route-customerId="@c.CustomerId">
                                <i class="material-icons icon">delete</i>
                            </a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>
<div>
    @{
        var prev = Model.PageSortResult.PreviousPage.HasValue ? "" : "disabled";
        var next = Model.PageSortResult.NextPage.HasValue ? "" : "disabled";
        var first = Model.PageNumber != 1 ? "" : "disabled";
        var last = Model.PageNumber != Model.PageSortResult.TotalPages ? "" : "disabled";
    }
</div>
<a asp-page="./List"
   asp-route-pageNumber="1"
   asp-route-PageSize="@Model.PageSize"
   asp-route-SortField="@Model.SortField"
   asp-route-SortDir="@Model.SortDir"
   class="btn @first">
    <i class="material-icons icon">first_page</i>
</a>
<a asp-page="./List"
   asp-route-pageNumber="@Model.PageSortResult.PreviousPage" 
   asp-route-PageSize="@Model.PageSize"
   asp-route-SortField="@Model.SortField"
   asp-route-SortDir="@Model.SortDir"
   class="btn @prev">
    <i class="material-icons icon">chevron_left</i>
</a>
Page @Model.PageNumber of @Model.PageSortResult.TotalPages
<a asp-page="./List"
   asp-route-pageNumber="@Model.PageSortResult.NextPage"
   asp-route-PageSize="@Model.PageSize"
   asp-route-SortField="@Model.SortField"
   asp-route-SortDir="@Model.SortDir"
   class="btn @next">
    <i class="material-icons icon">chevron_right</i>
</a>
<a asp-page="./List"
   asp-route-pageNumber="@Model.PageSortResult.TotalPages"
   asp-route-PageSize="@Model.PageSize"
   asp-route-SortField="@Model.SortField"
   asp-route-SortDir="@Model.SortDir"
   class="btn @last">
    <i class="material-icons icon">last_page</i>
</a>

就这些!希望您发现这在构建分页和排序应用程序时很有用。

https://www.codeproject.com/Articles/5292061/Paging-and-Sorting-using-ASP-NET-Core-Razor-Page-W

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值