65.9K
CodeProject 正在变化。 阅读更多。
Home

在 ASP.NET MVC 5 中使用服务器端过滤、排序和分页的 GridView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (63投票s)

2016年8月14日

CPOL

10分钟阅读

viewsIcon

161900

downloadIcon

7735

使用 jquery datatables 实现 GridView 服务器端分页、排序和过滤的初学者指南

引言

在这篇文章中,我们将探讨如何实现服务器端分页、搜索和排序。对于长期运行或数据集过大的应用程序来说,这当然是一种更好的方法。

我们将修改上一篇文章中的源代码来实现这一点,所以让我们开始吧。

背景

上一篇文章(ASP.NET MVC 5 中创建 GridView 的初学者指南)中,我们讨论了如何在 ASP.NET MVC 中实现类似于 ASP.NET Webforms 的 GridView 功能。我们看到了使用 jQuery datatables 插件实现网格是多么容易,它提供了搜索、排序和分页等重要功能。

上一篇文章中需要注意的一点是,插件提供的所有功能都是客户端的,这意味着所有数据都首先加载到页面中,然后插件在客户端处理数据以进行搜索、分页和排序。如果结果集不是很大,这没有问题,但如果表格太大,或者数据随着应用程序的使用逐渐增长,则可能会导致问题。如果出现这些问题,这种创建网格的方式从长远来看将会失败。

用于 MVC5 的 Datatables.net

首先,我们需要从 NuGet 包管理器安装 datatables.mvc5。它是由 Stefan Nuxoll 实现的 datatables 模型绑定器到控制器。为什么我们需要这个包?因为绑定器将提供一个强类型模型发布到控制器,这将帮助我们避免读取请求参数,并避免对 Request 中的参数进行类型转换。Request 对象中发布的所有参数都不是类型安全的,因此我们必须手动将它们转换为目标类型,这将帮助开发人员专注于业务逻辑,而不是处理 HTTP 参数、检查它们并转换为正确的类型。

这个绑定器的好处是,如果您的业务需求需要,您可以添加请求中发送的自定义参数。

您可以通过提供自己的 IDataTablesRequest 实现来添加自己的自定义参数,并且您还需要重写其 BindModelMapAdditionalColumns 方法。

数据库创建

现在让我们创建本文将要使用的数据库和表,打开 SQL Server Management Studio 并运行以下脚本

CREATE DATABASE [GridExampleMVC]  
 GO  
   
 CREATE TABLE [dbo].[Assets] (  
     [AssetID]                   UNIQUEIDENTIFIER NOT NULL,  
     [Barcode]                   NVARCHAR (MAX)   NULL,  
     [SerialNumber]              NVARCHAR (MAX)   NULL,  
     [FacilitySite]              NVARCHAR (MAX)   NULL,  
     [PMGuide]                   NVARCHAR (MAX)   NULL,  
     [AstID]                     NVARCHAR (MAX)   NOT NULL,  
     [ChildAsset]                NVARCHAR (MAX)   NULL,  
     [GeneralAssetDescription]   NVARCHAR (MAX)   NULL,  
     [SecondaryAssetDescription] NVARCHAR (MAX)   NULL,  
     [Quantity]                  INT              NOT NULL,  
     [Manufacturer]              NVARCHAR (MAX)   NULL,  
     [ModelNumber]               NVARCHAR (MAX)   NULL,  
     [Building]                  NVARCHAR (MAX)   NULL,  
     [Floor]                     NVARCHAR (MAX)   NULL,  
     [Corridor]                  NVARCHAR (MAX)   NULL,  
     [RoomNo]                    NVARCHAR (MAX)   NULL,  
     [MERNo]                     NVARCHAR (MAX)   NULL,  
     [EquipSystem]               NVARCHAR (MAX)   NULL,  
     [Comments]                  NVARCHAR (MAX)   NULL,  
     [Issued]                    BIT              NOT NULL,  
     CONSTRAINT [PK_dbo.Assets] PRIMARY KEY CLUSTERED ([AssetID] ASC)  
 )  
 GO

源代码中附带了一个完整的 SQL 脚本文件,因此您可以使用它来创建带有示例数据的数据库和表。

项目设置

现在,创建一个新的 ASP.NET MVC 5 Web 应用程序。打开 Visual Studio 2015。转到 文件 >> 新建 >> 项目

在对话框中,导航到“Web”并选择“ASP.NET Web 应用程序”项目,然后单击“确定”。

从模板中选择 MVC,如果您也要为您的实现编写单元测试,请勾选单元测试,然后单击 OK

我们的项目已创建,并已包含基本内容。现在,我们将首先创建数据库上下文类,因为我们将使用 Entity Framework 进行数据访问。

创建模型和数据访问

首先,我们需要为 Asset 表创建模型,我们将使用它通过 ORM 检索数据。
Model 文件夹中,创建一个名为 Asset 的新类

using System.ComponentModel.DataAnnotations;

namespace GridExampleMVC.Models
{
    public class Asset
    {
        public System.Guid AssetID { get; set; }

        [Display(Name = "Barcode")]
        public string Barcode { get; set; }

        [Display(Name = "Serial-Number")]
        public string SerialNumber { get; set; }

        [Display(Name = "Facility-Site")]
        public string FacilitySite { get; set; }

        [Display(Name = "PM-Guide-ID")]
        public string PMGuide { get; set; }

        [Required]
        [Display(Name = "Asset-ID")]
        public string AstID { get; set; }

        [Display(Name = "Child-Asset")]
        public string ChildAsset { get; set; }

        [Display(Name = "General-Asset-Description")]
        public string GeneralAssetDescription { get; set; }

        [Display(Name = "Secondary-Asset-Description")]
        public string SecondaryAssetDescription { get; set; }
        public int Quantity { get; set; }

        [Display(Name = "Manufacturer")]
        public string Manufacturer { get; set; }

        [Display(Name = "Model-Number")]
        public string ModelNumber { get; set; }

        [Display(Name = "Main-Location (Building)")]
        public string Building { get; set; }

        [Display(Name = "Sub-Location 1 (Floor)")]
        public string Floor { get; set; }

        [Display(Name = "Sub-Location 2 (Corridor)")]
        public string Corridor { get; set; }

        [Display(Name = "Sub-Location 3 (Room No)")]
        public string RoomNo { get; set; }

        [Display(Name = "Sub-Location 4 (MER#)")]
        public string MERNo { get; set; }

        [Display(Name = "Sub-Location 5 (Equip/System)")]
        public string EquipSystem { get; set; }

        public string Comments { get; set; }

        public bool Issued { get; set; }
    }
}

现在从 解决方案资源管理器 导航到 Models 文件夹并打开 IdentityModels.cs 文件。我们将在数据库上下文中的 Asset 表中添加一个属性,这将是使用脚本创建的 Asset 表的 Entity Framework 表示。在 ApplicationDbContext 类中添加新属性

public class ApplicationDbContext : IdentityDbContext<applicationuser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public DbSet<asset> Assets { get; set; }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

以上是 ASP.NET Identity 2.0 的默认实体框架设置,我们正在使用我们自己的表对其进行扩展,为此我们为 Asset 表添加了新的 DbSet

现在,在 Controllers 文件夹中添加一个名为 AssetController 的空控制器,我们将用它来处理所有与 Asset 相关的工作。它应该看起来像这样

public class AssetController : Controller
    {
        // GET: Asset
        public ActionResult Index()
        {
            return View();
        }

Jquery Datatables 的安装

现在,我们将安装 jQuery datatables,它将用于构建网格。转到 工具 >> NuGet 包管理器 >> 管理解决方案的 NuGet 包 并单击它。

包管理器将打开,默认情况下,它将显示解决方案中已安装的 NuGet 包。单击“浏览”按钮,然后搜索 jQuery datatables 包,然后选择它并选中您要安装此包的解决方案项目。在我们的例子中,我们根据要求将其仅安装在 GridExampleMVC Web 项目中,然后按 安装 按钮。

Visual Studio 将提示您告知它将修改解决方案,您需要按“确定”继续安装包。

NuGet 包成功安装后,我们需要在将使用它的视图中包含必要的 jscss 文件。为此,我们必须注册 jQuery datatables,打开位于 App_Start 文件夹中的 BundleConfig.cs 文件,并在末尾添加以下用于 cssjs 文件的代码

bundles.Add(new ScriptBundle("~/bundles/datatables").Include(
                        "~/Scripts/DataTables/jquery.dataTables.min.js",
                        "~/Scripts/DataTables/dataTables.bootstrap.js"));

bundles.Add(new StyleBundle("~/Content/datatables").Include(
          "~/Content/DataTables/css/dataTables.bootstrap.css"));

注册 Datatables 的脚本和 CSS 后,我们需要将它们添加到我们的主布局中,默认情况下是位于 Views >> Shared 中的 _Layout.cshtml,它在同一位置的 _ViewStart.cshtml 中定义。

安装 Datatables.net 包

现在,我们将安装 datatables.mvc5。转到 工具 >> NuGet 包管理器 >> 管理解决方案的 NuGet 包 并单击它。

包管理器将打开,默认情况下,它将显示解决方案中已安装的 NuGet 包。单击“浏览”按钮,然后搜索 datatable.mvc5 包,然后选择它并选中您要安装此包的解决方案项目。在我们的例子中,我们根据要求将其仅安装在 GridExampleMVC Web 项目中。然后按安装按钮。

在搜索结果中选择正确的包(如上图所示,它是返回的第一个)并安装它。

如果包安装成功,您将能够在项目的 引用 中看到它

配置数据库连接字符串

在编写控制器代码之前,我们需要为实体框架配置连接字符串,它将在进行数据库操作(即运行查询)时用于连接数据库。因此,我们的连接字符串应指向有效的数据源,以便我们的应用程序在运行时不会崩溃。

为此,打开 web.config 并提供数据库的连接字符串。在 config 文件中,您将在 configuration 节点下找到 connectionStrings,您需要根据您的系统修改该节点中的连接字符串。在我的例子中,它看起来像

<connectionstrings>
    <add connectionstring="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=GridExampleMVC;
     Integrated Security=True;MultipleActiveResultSets=true" name="DefaultConnection" 
     providername="System.Data.SqlClient"/>
</connectionstrings>

现在在控制器中,添加一个用于数据库上下文的属性,我们将使用它来查询数据库。

private ApplicationDbContext _dbContext;

public ApplicationDbContext DbContext
{
    get
    {
        return _dbContext ?? HttpContext.GetOwinContext().Get<applicationdbcontext>();
    }
    private set
    {
        _dbContext = value;
    }
}

我们将在控制器所有需要的操作中使用此属性,通过 Entity Framework 查询数据库。

转到 Index.cshtml 文件,通过删除表格的 theadtbody 元素来更新视图的 HTML。您更新后的 HTML 将是

<div class="row">
    <div class="col-md-12">
        <div class="panel panel-primary list-panel" id="list-panel">
            <div class="panel-heading list-panel-heading">
                <h1 class="panel-title list-panel-title">Assets</h1>
            </div>
            <div class="panel-body">
                <table id="assets-data-table" class="table table-striped table-bordered" 
                 style="width:100%;">
                </table>
            </div>
        </div>
    </div>
</div>

Jquery Datatables 初始化

我们删除了表格的 headbody,因为它将由 datatables 插件自身生成。现在我们必须更新 jQuery datatables 初始化,以便它通过 ajax 从服务器端加载数据。

为此,请在 Index.cshtml 视图中添加以下代码

@section Scripts
{    
<script type="text/javascript">
        var assetListVM;
        $(function () {
            assetListVM = {
                dt: null,

                init: function () {
                    dt = $('#assets-data-table').DataTable({
                        "serverSide": true,
                        "processing": true,
                        "ajax": {
                            "url": 
                            "@Url.Action("Get","Asset")"
                        },
                        "columns": [
                            { "title": "Bar Code", 
                            "data": "BarCode", 
                            "searchable": true },
                            { "title": "Manufacturer", 
                            "data": "Manufacturer", 
                            "searchable": true },
                            { "title": "Model", 
                            "data": "ModelNumber", 
                            "searchable": true },
                            { "title": "Building", 
                            "data": "Building", 
                            "searchable": true },
                            { "title": "Room No", 
                            "data": "RoomNo" },
                            { "title": "Quantity", 
                            "data": "Quantity" }
                        ],
                        "lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]],
                    });
                }
            }

            // initialize the datatables
            assetListVM.init();
        });

</script>    
}

我们已将 datatables 初始化代码编写在一个名为 init 的函数中,其中我们将 serverSide 属性设置为 true,这表示网格将是服务器端(分页、过滤和排序),因此现在所有记录都不会一次性加载,而是默认显示第一页的记录,并根据用户操作(无论是什么)加载更多数据。processing 属性用于在从操作检索数据时显示加载器,如果有人不想在加载数据时显示消息,可以将其消除,并且默认情况下,它将为 false。接下来,我们定义将作为 datatable 操作的回调的操作,之后,我们使用 columns 属性指定需要显示的列,lengthMenu 用于分页功能中每页的记录数。assetListVM.init(); 将在页面加载时调用,因为它写在 document.ready 中,是的,$(function () { }); 是它的简写形式。

安装 System.Linq.Dynamic 包

在此之后,我们将在 AssetController 中编写 Get 动作代码。为此,我们首先需要引用 System.Linq.Dynamic 命名空间,因为我们将在我们的动作中使用动态 Linq 提供的方法。再次转到 NuGet 包管理器,搜索 System.Linq.Dynamic 包并将其安装到您的项目中。

控制器中的排序、过滤和分页

安装包后,转到 AssetController 并编写 Get 动作实现,如下所示

public ActionResult Get([ModelBinder(typeof(DataTablesBinder))] IDataTablesRequest requestModel)
{
    IQueryable<asset> query = DbContext.Assets;
    var totalCount = query.Count();

    #region Filtering
    // Apply filters for searching
    if (requestModel.Search.Value != string.Empty)
    {
        var value = requestModel.Search.Value.Trim();
        query = query.Where(p => p.Barcode.Contains(value) ||
                                 p.Manufacturer.Contains(value) ||
                                 p.ModelNumber.Contains(value) ||
                                 p.Building.Contains(value)
                           );
     }

     var filteredCount = query.Count();

     #endregion Filtering

     #region Sorting
     // Sorting
     var sortedColumns = requestModel.Columns.GetSortedColumns();
     var orderByString = String.Empty;

     foreach (var column in sortedColumns)
     {
        orderByString += orderByString != String.Empty ? "," : "";
        orderByString += (column.Data) + 
          (column.SortDirection == 
          Column.OrderDirection.Ascendant ? " asc" : " desc");
     }

     query = query.OrderBy(orderByString == 
     string.Empty ? "BarCode asc" : orderByString);

     #endregion Sorting

     // Paging
     query = query.Skip(requestModel.Start).Take(requestModel.Length);

     var data = query.Select(asset => new
     {
        AssetID = asset.AssetID,
        BarCode = asset.Barcode,
        Manufacturer = asset.Manufacturer,
        ModelNumber = asset.ModelNumber,
        Building = asset.Building,
        RoomNo = asset.RoomNo,
        Quantity = asset.Quantity
     }).ToList();

     return Json(new DataTablesResponse
     (requestModel.Draw, data, filteredCount, totalCount), 
                 JsonRequestBehavior.AllowGet);
}

我们使用 Entity Framework 进行数据访问,但这并非强制要求,您也可以使用 ADO.NET 实现相同的功能,您唯一需要做的就是从操作中返回 JSON,并带有 DataTableResponse 的实例,如果脚本中正确定义了列,datatables 将能够正确显示您的数据。

我们正在获取 Assets 的引用,以便我们可以使用 Linq to Entities 查询获取数据,并且我们使用 Count() 获取 Assets 表的总记录数,因为这需要作为参数传递给 DataTablesResponse 的构造函数,后者是操作方法的最后一行。

IQueryable<asset> query = DbContext.Assets;

var totalCount = query.Count();

之后,我们编写了过滤逻辑,它将根据用户定义的标准过滤数据,代码不言自明,如下所示

if (requestModel.Search.Value != string.Empty) 
{ 
   var value = requestModel.Search.Value.Trim(); 
   query = query.Where(p => p.Barcode.Contains(value) || 
                            p.Manufacturer.Contains(value) || 
                            p.ModelNumber.Contains(value) || 
                            p.Building.Contains(value) ); 
}

我们正在做的是检查用户是否在文本框中指定了任何搜索条件,然后检查上面指定的所有四个列是否找到任何匹配的记录,这些条件将被返回。

之后,我们实现了排序逻辑,排序列信息发布在模型中,我们使用自定义模型绑定使用了该信息,并且使用 System.Linq.Dynamic,我们能够避免 ifswitch 语句,我们正在迭代用户应用排序的列,并根据它们对行进行排序

 var sortedColumns = requestModel.Columns.GetSortedColumns();
     var orderByString = String.Empty;

     foreach (var column in sortedColumns)
     {
        orderByString += orderByString != String.Empty ? "," : "";
        orderByString += (column.Data) + 
          (column.SortDirection == Column.OrderDirection.Ascendant ? " asc" : " desc");
     }

     query = query.OrderBy(orderByString == string.Empty ? "BarCode asc" : orderByString);

最后,我们应用分页部分并检查用户选择了哪个页面。默认情况下,第一页加载,之后,我们通过 requestModel.Start 跟踪用户所在的每个回调页面,以及用户点击了哪个页面,requestModel.Length 告诉用户每页希望看到多少条记录,这也可以通过页面上的组合框由用户配置。

现在构建项目,并在浏览器中运行它,以查看运行中的 GridView(具有服务器端过滤、分页和排序)的效果。

接下来(高级搜索)

本系列还有另一篇文章,讨论如何在此服务器端 jQuery DataTable 处理中添加高级搜索,因为目前正在使用单个 textbox,它搜索 Grid 的所有列或我们指定要搜索的列,但有时我们需要通过对每个列应用不同的搜索条件来实现更强大的搜索,本文演示了如何做到这一点

您可能还想阅读

 

© . All rights reserved.