目录
介绍
背景
创建项目
在后端处理分页
创建分页UI控件
添加搜索过滤器
自定义分页UI控件
改善性能
改善搜寻逻辑
- 下载样本-1.6 MB
本文将解释如何使用分页来仅检索所需数量的记录,并显示参考总记录的分页控件。
背景最有可能遇到了这个问题,您需要从包含数千个或更多记录的数据源中列出几行记录,但随后注意到分页是提高站点性能的重要因素。从过滤数据到从数据库中选择相关记录到显示页面调度控件,要构建一个可靠的页面调度系统,需要考虑一些很重要的步骤。
创建项目我将使用VS2019附带的默认ASP.NET Core 2.2项目模板,因此只需创建基本的ASP.NET Core 2.2项目并在此处继续阅读。
在深入探讨分页之前,我们需要为记录创建一个数据源。我们的数据源必须包含很多记录,以便我们可以看到分页控件的真正好处。为了使注意力集中在分页主题上,我将使用框架中已包含的项目列表(CultureInfo)。
打开Pages/Index.cshtml.cs并添加数据源,如下所示:
public class IndexModel : PageModel
{
public CultureInfo[] CulturesList { get; set; }
private CultureInfo[] Cultures { get; set; }
public IndexModel()
{
//this will act as the main data source for our project
Cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
}
public void OnGet()
{
CulturesList = Cultures;
}
}
打开Pages/Index.cshtml 并添加以下代码以显示区域性列表:
LCID
English Name
Native Name
Culture types
@foreach (var c in Model.CulturesList)
{
@c.LCID
@c.EnglishName
@c.NativeName
@c.CultureTypes
}
运行应用程序以查看第一个结果:
如您所见,我们正在将所有记录发送到视图,但这不是一种有效的编程方式,因此我们将限制所选记录的数量,以将少量数据发送到视图。基本上,我们需要两个用于分页的变量;
- 页码:用于指示请求的页码的变量
- 页面大小:一个变量,指示应该一次选择的记录总数
稍后,我们还将添加更多变量以进行过滤。
返回Pages/Index.cshtml.cs文件,定义变量并修改OnGet,所以我们的新IndexModel看起来如下所示:
public class IndexModel : PageModel
{
public IList CulturesList { get; set; }
private CultureInfo[] Cultures { get; set; }
//page number variable
[BindProperty(SupportsGet = true)]
public int P { get; set; } = 1;
//page size variable
[BindProperty(SupportsGet = true)]
public int S { get; set; } = 10;
public IndexModel()
{
//this will act as the main data source for our project
Cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
}
public void OnGet()
{
CulturesList = Cultures
//make sure to order items before paging
.OrderBy(x=>x.EnglishName)
//skip items before current page
.Skip((P-1)*S)
//take only 10 (page size) items
.Take(S)
//call ToList() at the end to execute the query and return the result set
.ToList();
}
}
运行该应用程序,您将仅看到前10条记录。
Bootstrap提供了一个非常不错的分页UI控件,但它仅呈现HTML元素,并且仍然需要进行大量工作才能知道在控件内呈现什么内容以及如何呈现,例如,记录总数,最大显示页数,搜索过滤器,根据当前页面索引等启用/禁用上一个——下一个按钮。
我将使用LazZiya.TagHelpers nuget包中的页面标记助手,它将为我们完成所有艰苦的工作。:)
PagingTagHelper 基本上需要这些参数:
- page-no:当前页码的必需int变量
- total-records:数据源中的总记录数所必需的int
- query-string-value:string值,如果URL中包含搜索过滤器,则为必填
- page-size:可选int(默认为10)
- query-string-key-page-no:可选,string指示页码的查询字符串键名称。默认值为"p",我们将不使用它,因为我们在后端定义了相同的键名。
- query-string-key-page-size:可选,string指示页面大小的查询字符串键名称。默认值为"s",所以我们不会使用它,因为我们也在后端定义了相同的键名。
在此处阅读有关PagingTagHelper的更多信息。
因此,在添加页面标记帮助器之前,我们需要再添加一个变量来处理后端的总记录数:
//total number of records
public int TotalRecords { get; set; } = 0;
public void OnGet()
{
TotalRecords = Cultures.Count();
CulturesList = Cultures
//make sure to order items before paging
.OrderBy(x=>x.EnglishName)
//skip items before current page
.Skip((P-1)*S)
//take only 10 (page size) items
.Take(S)
//call ToList() at the end to execute the query and return the result set
.ToList();
}
现在,我们准备使用分页标记帮助器。
使用软件包管理器控制台安装LazZiya.TagHelpers nuget软件包(请确保下载最新版本):
Install-Package LazZiya.TagHelpers -Version 2.2.1
或使用nuget包管理器UI:
添加LazZiya.TagHelpers到_ViewImports.cshtml页面:
@addTagHelper *, LazZiya.TagHelpers
将页面标记帮助程序代码添加到表下方的Index.cshtml视图中:
稍后,我们将在添加一些搜索过滤器之后添加query-string-value,现在,运行该应用并在其基本设置中测试分页控件:
记录的基本列表已完成,现在我们将添加一些搜索过滤器以具有更多功能列表。
首先,让我们将基本的文本搜索逻辑添加到后端:
//variable for text search
[BindProperty(SupportsGet = true)]
public string Q { get; set; } = string.Empty;
public void OnGet()
{
var query = Cultures
//search in EnglishName and NativeName
.Where(x =>
x.EnglishName.Contains(Q, StringComparison.OrdinalIgnoreCase) ||
x.NativeName.Contains(Q, StringComparison.OrdinalIgnoreCase));
//count records that returns after the search
TotalRecords = query.Count();
CulturesList = query
//make sure to order items before paging
.OrderBy(x => x.EnglishName)
//skip items before current page
.Skip((P - 1) * S)
//take only 10 (page size) items
.Take(S)
//call ToList() at the end to execute the query and return the result set
.ToList();
}
我们定义了一个名为"Q"的string变量,该变量将分配给搜索文本框。此外,我们修改了逻辑,以便TotalRecords值返回搜索后的记录数。
现在我们可以将搜索表单添加到前端:
Search
确保form方法是"get"因为我们在后端定位目标OnGet()方法,这将使我们能够共享任何编号页面的URL。
运行应用程序并测试搜索:
搜索效果很好,但是如果单击另一个页码,则会丢失搜索关键字!为了使所有查询字符串参数都包含在带编号的页面URL中,我们需要添加query-string-value到标签帮助器中:
现在,搜索和分页可以很好地协同工作。
可以通过添加更多控件(如页面总数标签,总记录标签和页面大小控件)来自定义我们的页面标记助手,并修改如下的页面标记助手代码以获取更多详细信息:
现在我们有了更多功能的分页控件:
到现在为止,我们将返回CultureInfo项列表,但是在表中仅显示了几个字段!因此,我们可以通过返回仅包含显示字段的对象列表来改善内存/带宽的使用。
创建一个名为CultureItem的新类,并修改搜索逻辑以返回CultureItem的列表,而不是CultureInfo:
//object that contains only displayed fields
public class CultureItem
{
public int LCID { get; set; }
public string EnglishName { get; set; }
public string NativeName { get; set; }
public CultureTypes CultureTypes { get; set; }
}
//return list of CultureItem
public IList CulturesList { get; set; }
public void OnGet()
{
var query = Cultures
//search in EnglishName and NativeName
.Where(x =>
x.EnglishName.Contains(Q, StringComparison.OrdinalIgnoreCase) ||
x.NativeName.Contains(Q, StringComparison.OrdinalIgnoreCase))
//map the selected fields to our new object
.Select(x => new CultureItem
{
LCID = x.LCID,
EnglishName = x.EnglishName,
NativeName = x.NativeName,
CultureTypes = x.CultureTypes
});
//count records that returns after the search
TotalRecords = query.Count();
CulturesList = query
//make sure to order items before paging
.OrderBy(x => x.EnglishName)
//skip items before current page
.Skip((P - 1) * S)
//take only 10 (page size) items
.Take(S)
//call ToList() at the end to execute the query and return the result set
.ToList();
}
改善搜寻逻辑
我们将关键字用作搜索中的一个文本字符串,我们可以通过拆分搜索关键字并删除空白和重复项来改善查询效果:
var _keyWords = Q.Split(new[] { ' ', ',', ':' },
StringSplitOptions.RemoveEmptyEntries).Distinct();
当使用MSSqlDb之类的数据库并在可为空的字段中进行搜索时,如果搜索到的字段为null,则可能会出现异常,为了避免在null字段中进行搜索,我们可以在搜索逻辑中添加null检查条件。
var query = Cultures
//search in EnglishName and NativeName
.Where(x => _keyWords.Any(kw =>
(x.EnglishName!=null && x.EnglishName.Contains
(kw, StringComparison.OrdinalIgnoreCase)) ||
(x.NativeName != null && x.NativeName.Contains
(kw, StringComparison.OrdinalIgnoreCase))))
通过在搜索数据库时使用AsNoTracking(),甚至可以进一步提高性能,因此框架不会继续跟踪所选实体,这将有助于释放一些内存。