分享

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

 码农9527 2021-04-28

  如何实现分页和排序以产生良好的性能

    如何使用.NETCoreRazor页面,WebAPI和实体框架实现分页和排序以产生良好的性能。

    该项目的特点是:

    选择页面大小

    页面导航

    显示记录号

    列排序

    核心课程

    第一件事是定义用户可以要求应用程序获取的内容:

    页面大小

    页码

    字段排序

    排序方向

    代码如下所示:

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
 }1234567891011121314复制代码类型:[html]

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

    记录总数

    总页数

    前一页编号-用户导航到前一页时的编号

    下一页编号-用于导航到下一页

    当前页面上的第一个记录号

    当前页面上的最后一条记录号

    代码如下所示:

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; }
 }123456789复制代码类型:[html]

    使用user参数和定义的结果,我们创建PageList<T>继承自的类,List<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
  }
 }123456789101112131415161718192021222324252627282930313233343536373839404142复制代码类型:[html]

    数据层

    的定义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; }
 }12345678910复制代码类型:[html]

    我们将定义常规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);
 }12345678复制代码类型:[html]

    并执行:

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;
  }
 }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657复制代码类型:[html]

    在DbContext从实体框架是简单的:

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

  public StarterDbContext(DbContextOptions<StarterDbContext> options)
   : base(options)
  {
  }
 }123456789复制代码类型:[html]

    API

    在WebAPI项目中,我们定义GetCustomers()将接受PageSortParam作为参数的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));
  }
 }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051复制代码类型:[html]

    剃刀页

    我们定义用户可以选择的页面大小:

public IEnumerable<SelectListItem> PageSizeList { get; set; } = 
  new SelectList(new List<int> { 5, 10, 25, 50 });12复制代码类型:[html]

    然后,我们[BindProperty(SupportsGet=true)]将值传递给HTML页面,然后又将值取回。对于每次旅行,我们都需要从HTML页面传递以下属性:

    PageSize–用户请求的页面大小

    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; }123456789101112131415复制代码类型:[html]

    该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());
   }
  }
 }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869复制代码类型:[html]

    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>123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122复制代码类型:[html]

    就这样!希望您发现这对构建页面调度和排序应用程序很有用。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多