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

在 ASP.NET MVC 5 中使用 JQuery DataTables 进行服务器端高级搜索的 Grid

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (12投票s)

2017 年 2 月 11 日

CPOL

9分钟阅读

viewsIcon

60151

downloadIcon

2975

本文演示了如何在 asp.net mvc 5 中使用 JQuery DataTables 在 Grid 上实现高级搜索

背景

在前两篇关于在 ASP.NET MVC 中实现 GridView 的帖子中,我们讨论了如何使用 JQuery DataTables 插件创建一个类似于 ASP.NET Web Forms 中的 Grid,然后在第二篇帖子中,我们看到了如何通过实现排序、搜索和分页来提高 Grid 的性能,因为在第一篇帖子中我们实现了一个 Grid,但问题是页面首次加载时,所有行都会作为 HTML 呈现,并且过滤、分页和排序都是在客户端进行的,由 datatables 插件处理。

如果您有兴趣阅读这些内容,可以点击下面的链接找到这两篇帖子

我希望在阅读了关于 ASP.NET MVC 中 Grid 的前几篇文章后,您现在能够更好地在 ASP.NET MVC 中创建 Grid View,这对于大多数初学者来说是一件困难的事情,特别是对于那些来自 Web Forms 开发经验的人来说。

引言

在这篇文章中,我们将看到如何为我们的 GridView 添加高级搜索,以便用户在搜索 Grid 中的数据时获得更友好的搜索体验。

我们不会重复之前已经完成的步骤,包括数据库创建和插入示例数据,使用所需的 nuget 包设置新 Web 应用程序项目,如果您直接阅读本文,您可能需要至少查看最后一篇关于服务器端过滤的帖子,以便熟悉我们的工作,因此,正如所说,我们将重复使用相同的项目和代码,并继续向其中添加新的部分。

在前一篇文章的最后,我们有一个带有服务器端分页、过滤和排序的 Grid,在 Grid 中实现高级搜索功能后,我们的应用程序将看起来像

步骤 1 - 数据库创建

我们在之前的帖子中看到,我们只有一个 Assets 表,用于在 Grid 中显示记录,并且所有数据都存储在一个非规范化的表中,因此我们对 Assets 表的一个列进行了规范化,并创建了一个名为 FacilitySitesLookup 表来演示如何使用 datatables 在服务器端实现高级搜索,规范化也主要是为了避免数据重复,这样,我们就可以将重复的值存储在另一个表中,并在其他表中引用唯一的标识符,而不是在多个行中重复相同的值。

以下是用于创建数据库的脚本

CREATE DATABASE [AdvancedSearchGridExampleMVC]
GO
CREATE TABLE [dbo].[FacilitySites] ([FacilitySiteID] UNIQUEIDENTIFIER NOT NULL,
                                    [FacilityName]   NVARCHAR (MAX)   NULL,
                                    [IsActive]       BIT              NOT NULL,
                                    [CreatedBy]      UNIQUEIDENTIFIER NOT NULL,
                                    [CreatedAt]      DATETIME         NOT NULL,
                                    [ModifiedBy]     UNIQUEIDENTIFIER NULL,
                                    [ModifiedAt]     DATETIME         NULL,
                                    [IsDeleted]      BIT              NOT NULL
                                   );
GO

CREATE TABLE [dbo].[Assets] (
                             [AssetID]                   UNIQUEIDENTIFIER NOT NULL,
                             [Barcode]                   NVARCHAR (MAX)   NULL,
                             [SerialNumber]              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,
                             [FacilitySiteID]            UNIQUEIDENTIFIER NOT NULL
                            );

GO

CREATE NONCLUSTERED INDEX [IX_FacilitySiteID]
   ON [dbo].[Assets]([FacilitySiteID] ASC);

GO
ALTER TABLE [dbo].[Assets]
    ADD CONSTRAINT [PK_dbo.Assets] PRIMARY KEY CLUSTERED ([AssetID] ASC);

GO

ALTER TABLE [dbo].[Assets]
   ADD CONSTRAINT [FK_dbo.Assets_dbo.FacilitySites_FacilitySiteID] FOREIGN KEY ([FacilitySiteID]) _
REFERENCES [dbo].[FacilitySites] ([FacilitySiteID]) ON DELETE CASCADE;

GO

如果数据库创建成功,之后我们需要向表中插入一些记录,以便当我们在应用程序中查询时,可以在页面上看到一些显示内容,以检查各项是否正常工作。所以以下是相应的脚本

INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'd37cc16b-3d13-4eba-8c98-0008b409a77b', N'D04-056',  
N'N/A', N'D-04', N'D04-056', N'N/A',  
N'DOOR, HYDR/ELEC/PNEUM OPERATED', N'N/A', 1, N'KM',  
N'N/A', N'South', N'7', N'E', N'019',  
N'', N'', N'Swing', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'7be68b37-5ec3-4a8b-be48-00490049f66b', N'C06-114',  
N'N/A', N'C-06', N'C06-114', N'A11-13,C08-16',  
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',  
N'N/A', N'South', N'9', N'F', N'004',  
N'MER5 ', N'AC-SE-2', N'rtn damper', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],  
[Issued], [FacilitySiteID])
VALUES (N'e8a8af59-a863-4757-93bd-00561f36122b', N'C03-069',  
N'N/A', N'C-03', N'C03-069', N'',  
N'COILS, REHEAT/PREHEAT (REMOTE)', N'N/A', 1, N'N/A',  
N'N/A', N'North', N'4', N'A', N'222',  
N'', N' RH-N-17', N'', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],  
[Issued], [FacilitySiteID]) VALUES (N'69dcdb07-8f60-4bbf-ad05-0078f3902c48',  
N'D06-300', N'N/A', N'D-06', N'D06-300',  
N'', N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1,  
N'N/A', N'N/A', N'South', N'Exterior', N'',  
N'1s0?', N'SB areaway 1st', N'', N'', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber], [Building],  
[Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments], [Issued],  
[FacilitySiteID]) VALUES (N'5b229566-5226-4e48-a6c7-008d435f81ae',  
N'A05-46', N'N/A', N'A-05', N'A05-46', N'',  
N'Air Conditioning Machine, Split System Chilled Water Coils',  
N'10 Tons and Under', 1, N'Trane', N'N/A', N'South',  
N'1', N'G', N'022', N'Headquarter Protective Force',  
N'', N'Above Ceilg', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription], [SecondaryAssetDescription],  
[Quantity], [Manufacturer], [ModelNumber], [Building], [Floor], [Corridor], [RoomNo],  
[MERNo], [EquipSystem], [Comments], [Issued], [FacilitySiteID])
VALUES (N'108d1792-7aa1-4865-a3d3-00a0ea973aa3', N'C06-252',  
N'N/A', N'C-06', N'C06-252', N'F27-35,C08-33',  
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',  
N'N/A', N'South', N'9', N'F', N'004',  
N'MER5 ', N'E-SE-1', N'exh damper', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],  
[Issued], [FacilitySiteID]) VALUES (N'80b9e4f9-71a4-4bd6-85c1-00a404cfee2b',  
N'D06-409', N'N/A', N'D-06', N'D06-409', N'',  
N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1, N'N/A',  
N'N/A', N'North', N'Exterior', N'',  
N'eas0?', N'NB lawn east', N'', N'', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'bdad32e0-9c21-4451-8cc9-00b47b155eb9', N'D04-182',  
N'N/A', N'D-04', N'D04-182', N'N/A', N'DOOR,  
HYDR/ELEC/PNEUM OPERATED', N'N/A', 1, N'N/A', N'N/A',  
N'South', N'2', N'E', N'2E-115',  
N'Bathrooms', N'', N'HYDR/ELEC/PNEUM', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'4d859a1b-10e0-4cb0-96a4-00c164a7237e', N'C03-222',  
N'N/A', N'C-03', N'C03-222', N'', N'COILS,  
REHEAT/PREHEAT (REMOTE)', N'N/A', 1, N'N/A',  
N'N/A', N'West', N'G', N'GJ, GI', N'086,052',  
N'MER8 ', N'SW-26', N'', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],  
[Issued], [FacilitySiteID]) VALUES (N'3df536d8-9f25-40dd-a83f-00c4434ad58e',  
N'D06-348', N'N/A', N'D-06', N'D06-348',  
N'', N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1,  
N'N/A', N'N/A', N'West', N'Exterior',  
N'', N'2n4?', N'WB areaway 2nd', N'',  
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'26c671bc-47f1-4d0e-acc6-00cdfb94b67d', N'C06-165',  
N'N/A', N'C-06', N'C06-165', N'A11-17,C08-22',  
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',  
N'N/A', N'South', N'9', N'F', N'004',  
N'MER5 ', N'AC-SE-6', N'min OA', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'be09535a-0fb6-4f7b-a74e-00dab4730211', N'D04-034',  
N'N/A', N'D-04', N'D04-034', N'N/A',  
N'DOOR, HYDR/ELEC/PNEUM OPERATED', N'N/A', 1,  
N'Dor-O-Matic, Jr', N'N/A', N'North', N'G',  
N'A', N'064', N'', N'', N'Swing', 0,  
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],  
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'65a0abaa-75cf-489a-9367-0118486218b9', N'D05-049',  
N'N/A', N'D-05', N'D05-049', N'N/A',  
N'DOOR, ENTRANCE, MAIN', N'N/A', 1,  
N'N/A', N'N/A', N'South', N'G_
1st', N'E', N'283', N'Ped Mall east', N'',  
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode],  
[SerialNumber], [PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],  
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],  
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],  
[Comments], [Issued], [FacilitySiteID])
VALUES (N'c0101cf3-d1f1-4d32-a4b5-0135dc54645a',  
N'C03-046', N'N/A', N'C-03', N'C03-046',  
N'', N'COILS, REHEAT/PREHEAT (REMOTE)', N'N/A', 1,  
N'N/A', N'N/A', N'North', N'5',  
N'A', N'084', N'', N'RH-N-30',  
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
GO

步骤 2 - 创建高级搜索表单

我们将为高级搜索创建一个新视图,其中将包含一个表单,该表单包含一些输入 HTML 控件,这些控件将被发布到控制器操作以过滤记录。

在解决方案资源管理器中,展开 Views 文件夹,然后再次展开 Asset 文件夹并打开 Index.cshtml 文件,我们将为 Grid 上方显示的高级搜索按钮添加 HTML。将以下 HTML 添加到视图中

<button type="button" 
class="btn btn-default btn-md" data-toggle="modal" 
 data-target="#advancedSearchModal" 
 id="advancedsearch-button">
 <span class="glyphicon glyphicon-search" 
 aria-hidden="true"></span> Advanced Search
</button>

如果您注意到,按钮代码中包含了一些新属性,您不必担心它们是用于 bootstrap 模态框的,因为点击按钮将打开一个模态对话框,用户可以在其中选择搜索条件并搜索结果。 data-toggle="modal" 属性决定此按钮将切换模态对话框,而 data-target="#advancedSearchModal" 指定页面上将显示为模态对话框的 HTML 元素。
Index.cshtml 中添加上述 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>
                <button type="button" 
                class="btn btn-default btn-md" 
                data-toggle="modal" 
                data-target="#advancedSearchModal" 
                id="advancedsearch-button">
                    <span class="glyphicon glyphicon-search" 
                    aria-hidden="true"></span> Advanced Search
                </button>
            </div>
            <div class="panel-body">
                <table id="assets-data-table" 
                class="table table-striped table-bordered" 
                style="width:100%;">
                </table>
            </div>
        </div>
    </div>
</div>

@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>    
 }

我们的模态弹出框最终将看起来像

步骤 3 - 使用 Entity Framework 添加模型

下一步是创建一个名为 FacilitySite 的新模型(DTO)类,该类将用于从上面数据库脚本创建的 FacilitySites 查找表中获取数据。因此,在解决方案资源管理器中 Models 文件夹中添加一个名为 FacilitySite 的新类,以下是其代码

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace GridAdvancedSearchMVC.Models
{
 public class FacilitySite
 {
 [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
 public System.Guid FacilitySiteID { get; set; }
 [Display(Name = "Facility-Site")]
 public string FacilityName { get; set; }
 public bool IsActive { get; set; }
 public System.Guid CreatedBy { get; set; }
 [Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
 public DateTime CreatedAt { get; set; }
 public System.Guid? ModifiedBy { get; set; }
 public DateTime? ModifiedAt { get; set; }
 public bool IsDeleted { get; set; }
 }
}

目前,我们只添加了 Model 类,它将保存 FacilitySites 表的数据,但由于我们使用 Entity Framework 进行数据访问,因此我们必须让它知道已添加了一个新表,可以在其上执行数据操作。为此,在 Models 文件夹中,打开 IdentityModel.cs 文件并将 ApplicationDbContext 代码更新为包含一个类型为 DbSet<FacilitySite> 的新属性

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

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

 public DbSet<FacilitySite> FacilitySites { get; set; }

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

同时更新 Asset 模型,删除之前类型为 StringFacilitySite 列,而是添加一个名为 FacilitySiteId 的新列,它将是 FacilitySite 表在 Asset 表中的外键,更新后的 Asset 模型应为

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace GridAdvancedSearchMVC.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; }
 [ForeignKey("FacilitySite")]
 public Guid FacilitySiteID { 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; }

 public virtual FacilitySite FacilitySite { get; set; }
 }
}

步骤 4 - 创建 ViewModel 类

我们还需要创建一个 ViewModel 类,用于将搜索条件发布到服务器端,以便控制器操作执行搜索。让我们然后添加 ViewModel。以下是 AdvancedSearchViewModel 类中的代码

using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace GridExampleMVC.Models
{
 public class AdvancedSearchViewModel
 {
 [Display(Name = "Facility-Site")]
 public Guid FacilitySite { get; set; }

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

 public string Manufacturer { get; set; }

 public string Status { get; set; }

 public SelectList FacilitySiteList { get; set; }
 public SelectList BuildingList { get; set; }
 public SelectList ManufacturerList { get; set; }
 public SelectList StatusList { get; set; }
 }
}

步骤 5 - 实现高级搜索 Get 操作

导航到 Controllers 文件夹并展开它,然后打开 AssetController.cs 文件,我们将添加一个新的 get 操作,用于填充 AdvancedSeachViewModel,我们将使用来自各自数据源的数据设置 SelectList 属性,以填充高级搜索模态弹出框上的下拉列表控件

[HttpGet]
public ActionResult AdvancedSearch()
{
 var advancedSearchViewModel = new AdvancedSearchViewModel();

 advancedSearchViewModel.FacilitySiteList = new SelectList(DbContext.FacilitySites
 .Where(facilitySite => facilitySite.IsActive && !facilitySite.IsDeleted)
 .Select(x => new { x.FacilitySiteID, x.FacilityName }),
 "FacilitySiteID",
 "FacilityName");

 advancedSearchViewModel.BuildingList = new SelectList(DbContext.Assets
 .GroupBy(x => x.Building)
 .Where(x => x.Key != null && !x.Key.Equals(string.Empty))
 .Select(x => new { Building = x.Key }),
 "Building",
 "Building");

 advancedSearchViewModel.ManufacturerList = new SelectList(DbContext.Assets
 .GroupBy(x => x.Manufacturer)
 .Where(x => x.Key != null && !x.Key.Equals(string.Empty))
 .Select(x => new { Manufacturer = x.Key }),
 "Manufacturer",
 "Manufacturer");

 advancedSearchViewModel.StatusList = new SelectList(new List<SelectListItem>
 {
 new SelectListItem { Text="Issued",Value=bool.TrueString},
 new SelectListItem { Text="Not Issued",Value = bool.FalseString}
 },
 "Value",
 "Text"
 );

 return View("_AdvancedSearchPartial", advancedSearchViewModel);
}

步骤 6 - 实现高级搜索 Post 操作

我们的 AdvancedSearch post 操作在实现上将与之前用于服务器端排序、过滤和分页的 Search 操作几乎相同,但 AdvancedSearch 的操作签名将有一个小变化,它现在将接受 2 个参数,这是很明显的,一个用于维护 DataTables 状态,这之前也是存在的,而新的参数将是 AdvancedSearchViewModel 类的一个实例,该实例将包含高级搜索模态弹出框控件的状态。

我们需要更新我们在上一篇关于 Grid View 服务器端处理的帖子中创建的 SearchAssets private 方法,将高级搜索数据库逻辑添加到此方法中,因此此方法将不再接受另一个参数,我们知道它是 AdvancedSearchViewModel 的实例

private IQueryable<Asset> SearchAssets(IDataTablesRequest requestModel, 
AdvancedSearchViewModel searchViewModel, IQueryable<Asset> query)
        {
            // Apply filters
            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)
                                   );
            }

            /***** Advanced Search Starts ******/
            if (searchViewModel.FacilitySite != Guid.Empty)
                query = query.Where(x => x.FacilitySiteID == searchViewModel.FacilitySite);

            if (searchViewModel.Building != null)
                query = query.Where(x => x.Building == searchViewModel.Building);

            if (searchViewModel.Manufacturer != null)
                query = query.Where(x => x.Manufacturer == searchViewModel.Manufacturer);

            if (searchViewModel.Status != null)
            {
                bool Issued = bool.Parse(searchViewModel.Status);
                query = query.Where(x => x.Issued == Issued);
            }

            /***** Advanced Search Ends ******/

            var filteredCount = query.Count();

            // Sort
            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);

            return query;
        }

步骤 7 - 更新 DataTables Post 调用操作

现在也更新用于处理 Grid 服务器端处理的操作,使其能够接受高级搜索参数,并将它们传递给 SearchAssets 方法以进行更精细化的过滤,以下是该操作的更新代码

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

 // searching and sorting
 query = SearchAssets(requestModel, searchViewModel,query);
 var filteredCount = query.Count();

 // 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);
}

步骤 8 - 实现高级搜索的模态弹出视图

现在,我们将转向视图部分,正如您所见,我们有最后四个 SelectList 类型的属性,因为我们将在高级表单中有几个下拉列表控件,用户将从中选择预先填充的值来搜索记录。

我们在 Index.cshtml 视图的 HTML 中添加的 data-target="#advancedSearchModal" 将在此部分视图中被引用,因此在 View >> Asset 下创建一个名为 _AdvancedSearchPartial 的新部分视图,为此,右键单击 View 下的 Asset 文件夹,然后导航到 Add Item,然后从下一个菜单中选择 MVC 5 Partial Page (Razor)

输入部分视图的名称,在这种情况下是 _AdvancedSearchPartial,然后单击 OK 按钮

然后,打开文件 _AdvancedSearchPartial.cshtml,并在部分视图中添加 HTML,当用户单击我们在 Index.cshtml 视图中创建的高级搜索按钮时,将显示此 HTML 作为模态弹出框,以下是高级搜索部分视图的代码

@model TA_UM.ViewModels.AdvancedSearchViewModel
@{
 Layout = null;
}

<div class="modal fade" id="advancedSearchModal" 
tabindex="-1" role="dialog" 
aria-labelledby="myModalLabel" 
aria-hidden="true" data-backdrop="static">
 <div class="modal-dialog">
 <div class="modal-content">
 <div class="modal-header">
 <h4 class="modal-title">Advanced Search</h4>
 </div>
 @using (Html.BeginForm("Get", "Asset", 
 FormMethod.Get, new { id = "frmAdvancedSearch", 
 @class = "form-horizontal", role = "form" }))
 {
 <div class="modal-body">
 <div class="form-horizontal">
 <hr />
 <div class="form-group">
 @Html.LabelFor(model => model.FacilitySite, 
 htmlAttributes: new { @class = "control-label col-md-3" })
 <div class="col-md-8">
 <div class="dropdown">
 @Html.DropDownListFor(model => model.FacilitySite, 
 Model.FacilitySiteList, "Any", new { @class = "form-control" })
 </div>
 </div>
 </div>

 <div class="form-group">
 @Html.LabelFor(model => model.Building, 
 htmlAttributes: new { @class = "control-label col-md-3" })
 <div class="col-md-8">
 <div class="dropdown">
 @Html.DropDownListFor(model => model.Building, 
 Model.BuildingList, "Any", new { @class = "form-control" })
 </div>
 </div>
 </div>

 <div class="form-group">
 @Html.LabelFor(model => model.Manufacturer, 
 htmlAttributes: new { @class = "control-label col-md-3" })
 <div class="col-md-8">
 <div class="dropdown">
 @Html.DropDownListFor(model => model.Manufacturer, 
 Model.ManufacturerList, "Any", new { @class = "form-control" })
 </div>
 </div>
 </div>

 <div class="form-group">
 @Html.LabelFor(model => model.Status, 
 htmlAttributes: new { @class = "control-label col-md-3" })
 <div class="col-md-8">
 <div class="dropdown">
 @Html.DropDownListFor(model => model.Status, 
 Model.StatusList, "Both", new { @class = "form-control" })
 </div>
 </div>
 </div>
 </div>
 </div>
 <div class="modal-footer">
 <button id="btnPerformAdvancedSearch" 
 type="button" class="btn btn-default btn-success" 
 data-dismiss="modal">Search</button>
 <button id="btnCancel" type="button" 
 class="btn btn-default" data-dismiss="modal">Cancel</button>
 </div>
 }
 </div>
 </div>
</div>

最后一步 - 从视图中的 POST 传递高级搜索参数

最后,打开位于 Views >> AssetIndex.cshtml 文件,并在 <span style="background-color: yellow">@section Scripts</span> 开始之前调用 AdvancedSearch get 操作,以在浏览器中添加将显示在按钮触发时的高级搜索模态弹出 HTML,另一件需要注意的是,我们在任何地方都没有指定下拉列表选定的值将如何与 DataTables 服务器端处理在同一个操作中发布,尽管我们已经在操作中添加了参数,但我们没有在视图中进行任何特定更改,我们将不得不为此更新 jquery datatables 初始化代码,并使用 data 属性指定要发布到 AdvancedSearchViewModel 的值,为此我们需要定义属性,因此请在指定 datatable 的 URL 的行之后添加以下代码,即 "url": "@Url.Action("Get","Asset")",在添加完最后的 Index 视图后,代码应为

"data": function (data) {

 data.FacilitySite = $("#FacilitySite").val();
 data.Building = $("#Building").val();
 data.Manufacturer = $("#Manufacturer").val();
 data.Status = $("#Status").val();
 }

我们的 Index 视图将包含以下代码

<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>
 <button type="button" 
 class="btn btn-default btn-md" data-toggle="modal" 
 data-target="#advancedSearchModal" id="advancedsearch-button">
 <span class="glyphicon glyphicon-search" 
 aria-hidden="true"></span> Advanced Search
 </button>
 </div>
 <div class="panel-body">
 <table id="assets-data-table" 
 class="table table-striped table-bordered" 
 style="width:100%;">
 </table>
 </div>
 </div>
 </div>
</div>

@Html.Action("AdvancedSearch")

@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")",
 "data": function (data) {

 data.FacilitySite = $("#FacilitySite").val();
 data.Building = $("#Building").val();
 data.Manufacturer = $("#Manufacturer").val();
 data.Status = $("#Status").val();
 }
 },
 "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]],
 });
 },

 refresh: function () {
 dt.ajax.reload();
 }
 }

 // Advanced Search Modal Search button click handler 
 $('#btnPerformAdvancedSearch').on("click", assetListVM.refresh);
 }

 // initialize the datatables
 assetListVM.init();

        });

</script>    
 }

您可以看到上面我们在 datatable 视图模型中添加了一个名为 refresh 的新函数,其目的是使用 DataTables 的模型从服务器端重新加载 datatable,我们为高级搜索弹出按钮编写了事件处理程序,当按下该按钮时,它会导致 datatable 重新加载,并在其 Ajax 调用中,我们使用 jQuery datatables 的 data 属性传递用户从高级搜索视图中选择的搜索条件。

现在构建项目,并在浏览器中运行它,以查看正在工作的服务器端高级搜索,使用 JQuery datatables 以及服务器端过滤、分页和排序。

本系列更多文章

© . All rights reserved.