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

ASP.NET 8 使用 DataTables.net – 第一部分 – 基础

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2024年7月10日

CPOL

9分钟阅读

viewsIcon

8380

downloadIcon

315

一份关于在 ASP.NET 8 MVC 应用程序中使用 jQuery DataTables.net 组件的实用指南。

1 ASP.NET8 使用 jQuery DataTables.net

我一直在为我的新项目(ASP.NET 8、C#、MVC、Bootstrap 5、EF)寻找一个适合我的开发环境的免费表格组件。在许多地方,我看到了 jQuery DataTables.net 组件 [1] 的提及。在浏览了不同的文章后,我决定创建几个 原型(概念验证)应用程序 来评估它是否适合在我的专业项目中使用。这些文章是我评估的结果。

 

1.1 本系列文章

本系列文章包括:

  • ASP.NET8 使用 DataTables.net – 第1部分 – 基础
  • ASP.NET8 使用 DataTables.net – 第2部分 – 操作按钮
  • ASP.NET8 使用 DataTables.net – 第3部分 – 状态保存
  • ASP.NET8 使用 DataTables.net – 第4部分 – 多语言
  • ASP.NET8 使用 DataTables.net – 第5部分 – 在 AJAX 中传递附加参数
  • ASP.NET8 使用 DataTables.net – 第6部分 – 在 AJAX 中返回附加参数
  • ASP.NET8 使用 DataTables.net – 第7部分 – 常规按钮
  • ASP.NET8 使用 DataTables.net – 第8部分 – 选择行
  • ASP.NET8 使用 DataTables.net – 第9部分 – 高级筛选器

 

2 最终结果

现在,让我们在 ASP.NET 8、C#、MVC、Bootstrap 5 环境中展示本文原型测试的结果。这是您将获得的内容:

您将获得一个外观漂亮的表格,它通过后台 AJAX 调用获取数据。您在图片中看到的表格本身是一个 jQuery 组件,您可以用 C# 编写后端处理。与图片中任何 绿色区域 的交互都会触发一个新的 AJAX 调用到 ASP.NET 服务器,并带上新参数,服务器会返回新数据集。

好处是您无需编写所有 HTML/JavaScript 来创建表格,这些由 DataTables.net 组件提供给您。当然,您需要学习组件的 API,并且在组件提供的 UI 选项方面会受到一些限制。

 

2.1 积极印象,但有所保留

我的印象总体是积极的,我将在我的项目中使用 DataTables.net 组件。其逻辑是,我需要这样一个组件来呈现我的表格,如果不用这个,我将需要开发我自己的版本,具有类似的功能来呈现我的表格。

然而,并非一切都尽如人意。首先,我已经遇到了 DataTables.net 的一个 bug,我已经报告了它,并依赖开源社区来解决它。其次,并非所有都是免费的,Editor 插件是商业化的,单用户许可证/年大约为 119 美元。

对于 C# 使用来说,问题在于 DataTables.AspNet.Core 库 [2], [3] 自 2022 年以来已不再维护。这是我在 ASP.NET/C# 端用于后端处理的开源库。存在一些 bug,您需要自行解决。

 

3 什么是 DataTables.net

DataTables.net 是一个 jQuery UI 库,在网站 [1] 上有详细描述,因此我在这里不再赘述。仅提及它有两种工作模式:

  1. 客户端处理
  2. 服务器端处理

在模式 1) 中,所有表格数据(假设 1000 行)由组件加载到浏览器中,并且所有过滤和排序都在客户端浏览器中使用 JavaScript 完成。从专业使用的角度来看,这是一种比较简单的工作模式。

在模式 2) 中,组件通过 AJAX 调用加载将要显示的数据(假设 15 行),并且在每次用户与组件交互后,都会发出新的 AJAX 调用以获取新数据。在本文中,我们只关注这种工作模式,这是唯一适合专业使用的模式。

 

3.1 如何获取 DataTables.net

网站 [1] 描述了获取该组件的几种方法,包括 CDN 和 NuGet 包。对我来说,并非所有方法都足够好,所以我发现最适合我的方法是直接从网站下载并直接部署到项目中。

 

<!-- _LoadingDatatablesJsAndCss.cshtml -->
<link rel="stylesheet" href="~/lib/datatables/datatables.min.css" asp-append-version="true" />
<script src="~/lib/datatables/datatables.min.js" defer asp-append-version="true"></script>

 

3.2 参考资料

在处理开源项目时,有必要收集所有可用的参考资料。我列出了一个参考列表,以下是其概要:

  • DataTables.net 组件在 [1]。有一个很棒的手册描述了 API 和所有选项。
  • DataTables.AspNet.Core 库在 [2], [3] 中有描述。这是用于 ASP.NET 后端处理的 C#/.NET 库。在 GitHub 网站上有一些使用它的示例。
  • 各种文章和示例 来自 [4]-[11]。如果您发现本教程太难,您可能会发现其中的其他文章更容易上手。
  • System.Linq.Dynamic.Core 库在 [12]-[14] 中有描述。这是一个有用的 C# 后端处理库。

 

4 集成教程

本教程的重点是在 ASP.NET 8、C#、MVC、Bootstrap 5、JavaScript、jQuery 环境中集成各种组件。我不会过多谈论单个组件,它们在其他地方已经有所描述。我也不打算在这里重复所有内容。

4.1 专业人士教程

我不会假装这是一个简单的课题。这是为经验丰富的专业人士准备的教程。我只会指出我前进的方向和额外的参考资料,仅此而已。在这里详细介绍所有内容是在浪费时间和精力。我假设您已经了解 AJAX、Linq、JavaScript、MVC、ASP.NET 等。

 

5 模拟数据库

我想在我的原型中在 Web UI 的 HTML 表格中展示的是我的 Employee 实体。为了使示例不依赖于任何数据库设置,我创建了一个内存中的模拟数据库,基本上是一个包含 1000 行的 JSON。代码如下:

//Employee.cs =========================================================
namespace Example01.MockDatabase
{
    public class Employee
    {
        public int Id { get; set; } = 0;
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
        public string? City { get; set; }
        public string? Country { get; set; }
        public string? Email { get; set; }
        public string? Phone { get; set; }
    }
}

//MockDatabase.cs ======================================================
namespace Example01.MockDatabase
{
    public class MockDatabase
    {
        //table EmployeesTable as an instance
        private List<Employee>? _employeeTable;
        public List<Employee> EmployeesTable
        {
            get
            {
                //going for singleton pattern
                if( _employeeTable == null )
                {
                    Employee[]? employeeArray = JsonSerializer.Deserialize<Employee[]>(MockEmployeeData.Data);
                    if( employeeArray != null )
                    {
                        _employeeTable = new List<Employee>();
                        _employeeTable.AddRange( employeeArray );
                    }
                }

                if (_employeeTable == null) { _employeeTable = new List<Employee>(); };
                return _employeeTable;
            }
        }

        //database MockDatabase as an instance
        private static MockDatabase? _instance;
        public static MockDatabase Instance { 
            get 
            {
                //going for singleton pattern
                if (_instance==null)
                {
                    _instance = new MockDatabase();
                }
                return _instance; 
            } 
        }
    }
}

//MockEmployeeData.cs =========================================
namespace Example01.MockDatabase
{
    public  static class MockEmployeeData
    {
        public static string Data=
            @"
[{""Id"":1,""FirstName"":""Saunder"",""LastName"":""Hedger"",""City"":""Poitiers"",""Country"":""France"",""Email"":""shedger0@naver.com"",""Phone"":""170-116-2494""},
{""Id"":2,""FirstName"":""Cornell"",""LastName"":""De la Zenne"",""City"":""San Antonio de los Cobres"",""Country"":""Argentina"",""Email"":""cdelazenne1@narod.ru"",""Phone"":""594-372-0412""},
{""Id"":3,""FirstName"":""Caryn"",""LastName"":""Scarsbrooke"",""City"":""Pelasgía"",""Country"":""Greece"",""Email"":""cscarsbrooke2@sciencedaily.com"",""Phone"":""184-707-0966""},
{""Id"":4,""FirstName"":""Graham"",""LastName"":""Sommerfeld"",""City"":""Nijmegen"",""Country"":""Netherlands"",""Email"":""gsommerfeld3@google.ru"",""Phone"":""927-828-0615""},
{""Id"":5,""FirstName"":""Rosy"",""LastName"":""Sparway"",""City"":""Wiesbaden"",""Country"":""Germany"",""Email"":""rsparway4@goo.gl"",""Phone"":""877-176-1489""},
{""Id"":6,""FirstName"":""Lesli"",""LastName"":""Heaton"",""City"":""Vilhena"",""Country"":""Brazil"",""Email"":""lheaton5@hugedomains.com"",""Phone"":""540-215-7615""},
{""Id"":7,""FirstName"":""Kevon"",""LastName"":""Rumble"",""City"":""Yangxi"",""Country"":""China"",""Email"":""krumble6@usatoday.com"",""Phone"":""805-371-5794""},
{""Id"":8,""FirstName"":""Keefe"",""LastName"":""Ainsley"",""City"":""Cabriz"",""Country"":""Portugal"",""Email"":""kainsley7@jugem.jp"",""Phone"":""140-378-1955""},
{""Id"":9,""FirstName"":""Ninon"",""LastName"":""Baptist"",""City"":""Susapaya"",""Country"":""Peru"",""Email"":""nbaptist8@si.edu"",""Phone"":""111-388-0159""},
{""Id"":10,""FirstName"":""Moira"",""LastName"":""Edwinson"",""City"":""Wangchang"",""Country"":""China"",""Email"":""medwinson9@sphinn.com"",""Phone"":""844-869-9886""},
{""Id"":11,""FirstName"":""Isidoro"",""LastName"":""Meach"",""City"":""Qinglin"",""Country"":""China"",""Email"":""imeacha@huffingtonpost.com"",""Phone"":""951-769-9045""},
{""Id"":12,""FirstName"":""Norry"",""LastName"":""Leggan"",""City"":""Malummaduri"",""Country"":""Nigeria"",""Email"":""nlegganb@altervista.org"",""Phone"":""601-917-3867""},
.....etc...1000 records

6 客户端 DataTables.net 组件

在这里,我将只展示使用 DataTables 组件的 ASP.NET 视图是什么样的。我不会在这里详细介绍所有配置属性,它们在 [1] 的手册中都有很好的记录。我假设您会从 [1] 中学到一些关于组件和配置的知识。我在这里只是展示如何将其与 ASP.NET 集成。我添加了一些注释。我假设您可以阅读 JavaScript/jQuery 代码。

编程风格各异,我选择了我认为最适合我专业项目的风格。我更喜欢明确列出的内容,并非所有人都喜欢这种风格。

请注意 AJAX 的配置部分。该组件通过发出 AJAX 调用来响应用户输入并获取正确的数据。

<!-- Employees.cshtml -->
<partial name="_LoadingDatatablesJsAndCss" />

@{
    <div class="text-center">
        <h3 class="display-4">Employees table</h3>
    </div>

    <!-- Here is our table HTML element defined. JavaScript library Datatables
    will do all the magic to turn it into interactive component -->
    <table id="EmployeesTable01" class="table table-striped table-bordered ">
    </table>
}

<script type="text/javascript">
    // Datatables script initialization =========================================
    // we used defer attribute on jQuery so it might not be available at this point
    // so we go for vanilla JS event

    document.addEventListener("DOMContentLoaded", InitializeDatatable);

    function InitializeDatatable() {
        $("#EmployeesTable01").dataTable(
            //providing initialization parameters as JavaScript object
            {
                //processing-Feature control the processing indicator.
                processing: true,
                //paging-Enable or disable table pagination.
                paging: true,
                //info-Feature control table information display field
                info: true,
                //ordering-Feature control ordering (sorting) abilities in DataTables.
                ordering: true,
                //searching-Feature control search (filtering) abilities
                searching: true,
                //search.return-Enable / disable DataTables' search on return.
                search: {
                    return: true
                },
                //autoWidth-Feature control DataTables' smart column width handling.
                autoWidth: true,
                //lengthMenu-Change the options in the page length select list.
                lengthMenu: [10, 15, 25, 50, 100],
                //pageLength-Change the initial page length (number of rows per page)
                pageLength: 15,

                //order-Initial order (sort) to apply to the table.
                order: [[1, 'asc']],

                //serverSide-Feature control DataTables' server-side processing mode.
                serverSide: true,

                //Load data for the table's content from an Ajax source.
                ajax: {
                    "url": "@Url.Action("EmployeesDT", "Home")",
                    "type": "POST",
                    "datatype": "json"
                },

                //Set column specific initialisation properties.
                columns: [
                    //name-Set a descriptive name for a column
                    //data-Set the data source for the column from the rows data object / array
                    //title-Set the column title
                    //orderable-Enable or disable ordering on this column
                    //searchable-Enable or disable search on the data in this column
                    //type-Set the column type - used for filtering and sorting string processing
                    //visible-Enable or disable the display of this column.
                    //width-Column width assignment.
                    //render-Render (process) the data for use in the table.
                    //className-Class to assign to each cell in the column.

                    {   //0
                        name: 'id',
                        data: 'id',
                        title: "Employee Id",
                        orderable: true,
                        searchable: false,
                        type: 'num',
                        visible: false
                    },
                    {
                        //1
                        name: 'givenName',
                        data: "givenName",
                        title: "Given Name",
                        orderable: true,
                        searchable: true,
                        type: 'string',
                        visible: true
                    },
                    {
                        //2
                        name: 'familyName',
                        data: "familyName",
                        title: "Family Name",
                        orderable: true,
                        searchable: true,
                        type: 'string',
                        visible: true
                    },
                    {
                        //3
                        name: 'town',
                        data: "town",
                        title: "Town",
                        orderable: true,
                        searchable: true,
                        type: 'string',
                        visible: true
                    },
                    {
                        //4
                        name: 'country',
                        data: "country",
                        title: "Country",
                        orderable: true,
                        searchable: true,
                        type: 'string',
                        visible: true,
                        width: "150px",
                        className: 'text-center '
                    },
                    {
                        //5
                        name: 'email',
                        data: "email",
                        title: "Email",
                        orderable: true,
                        searchable: true,
                        type: 'string',
                        visible: true
                    },
                    {
                        //6
                        name: 'phoneNo',
                        data: "phoneNo",
                        title: "Phone Number",
                        orderable: false,
                        searchable: true,
                        type: 'string',
                        visible: true
                    }
                ]
            } // end of initialization object
        );
    }
</script>

 

7 示例 AJAX 调用

为了更好地理解正在发生的事情,我使用了 Chrome DevTools 来捕获示例 AJAX 调用中的出站和入站数据。

这是 请求

draw: 2
columns[0][data]: id
columns[0][name]: id
columns[0][searchable]: false
columns[0][orderable]: true
columns[0][search][value]: 
columns[0][search][regex]: false
columns[1][data]: givenName
columns[1][name]: givenName
columns[1][searchable]: true
columns[1][orderable]: true
columns[1][search][value]: 
columns[1][search][regex]: false
columns[2][data]: familyName
columns[2][name]: familyName
columns[2][searchable]: true
columns[2][orderable]: true
columns[2][search][value]: 
columns[2][search][regex]: false
columns[3][data]: town
columns[3][name]: town
columns[3][searchable]: true
columns[3][orderable]: true
columns[3][search][value]: 
columns[3][search][regex]: false
columns[4][data]: country
columns[4][name]: country
columns[4][searchable]: true
columns[4][orderable]: true
columns[4][search][value]: 
columns[4][search][regex]: false
columns[5][data]: email
columns[5][name]: email
columns[5][searchable]: true
columns[5][orderable]: true
columns[5][search][value]: 
columns[5][search][regex]: false
columns[6][data]: phoneNo
columns[6][name]: phoneNo
columns[6][searchable]: true
columns[6][orderable]: false
columns[6][search][value]: 
columns[6][search][regex]: false
order[0][column]: 1
order[0][dir]: asc
order[0][name]: givenName
start: 0
length: 15
search[value]: mark
search[regex]: false

这是 响应

{
    "draw": 2,
    "recordsTotal": 1000,
    "recordsFiltered": 7,
    "data": [
        {
            "id": 867,
            "givenName": "Ambur",
            "familyName": "Key",
            "town": "San Pedro de Macorís",
            "country": "Dominican Republic",
            "email": "akeyo2@domainmarket.com",
            "phoneNo": "196-987-6349"
        },
        {
            "id": 224,
            "givenName": "Artus",
            "familyName": "Ledekker",
            "town": "Renxian",
            "country": "China",
            "email": "aledekker67@domainmarket.com",
            "phoneNo": "480-208-2782"
        },
        {
            "id": 60,
            "givenName": "Eolanda",
            "familyName": "Briamo",
            "town": "Francisco I Madero",
            "country": "Mexico",
            "email": "ebriamo1n@domainmarket.com",
            "phoneNo": "467-917-3423"
        },
        {
            "id": 15,
            "givenName": "Ermanno",
            "familyName": "Sirett",
            "town": "København",
            "country": "Denmark",
            "email": "esirette@about.com",
            "phoneNo": "249-103-3769"
        },
        {
            "id": 822,
            "givenName": "Jany",
            "familyName": "Cleen",
            "town": "Shādegān",
            "country": "Iran",
            "email": "jcleenmt@marketwatch.com",
            "phoneNo": "605-915-2463"
        },
        {
            "id": 416,
            "givenName": "Sunshine",
            "familyName": "Sandbrook",
            "town": "Shigu",
            "country": "China",
            "email": "ssandbrookbj@marketwatch.com",
            "phoneNo": "479-324-0262"
        },
        {
            "id": 406,
            "givenName": "Teresina",
            "familyName": "Sreenan",
            "town": "Staryy Saltiv",
            "country": "Ukraine",
            "email": "tsreenanb9@domainmarket.com",
            "phoneNo": "278-996-2943"
        }
    ]
}

这是生成的表格

我当然只提取了相关的数据部分。 每一个优秀的 Web 程序员都应该能够从上面的数据中理解发生了什么。

 

8 DataTables.AspNet.Core 库

如果您是 ASP.NET 后端程序员,那么来自上述示例 AJAX 调用的问题是如何在服务器端解析所有这些输入数据。

答案是,我正在使用 DataTables.AspNet.Core 库 [2], [3]。该库既是解决方案也是问题。它免费提供了有用的实用工具,但自 2022 年以来已不再维护。我的想法是,如果我不使用该库,我将需要编写自己的版本,可能最终会得出非常相似的设计解决方案。因此,我决定使用它并在需要时进行修补。

如何学习这个库?没有教程可用。因此,我只是从 GitHub 下载了源代码,并从源代码中学习。在同一个网站上也有一些作为演示的示例。

从现在开始,我将在本文的代码中仅使用该库。

 

9 System.Linq.Dynamic.Core 库

还有一个库对本应用程序的后端处理很有用,那就是 System.Linq.Dynamic.Core 库 [12]-[14]。[14] 提供了一个很棒的教程。从现在开始,我将在本文的代码中仅使用该库。

 

10 ASP.NET 后端处理

现在我们进入 C#/.NET 部分,编写我们的 ASP.NET 代码。这是我提出的解决方案。我不会假装它很简单,它假设您对 DataTables.AspNet.Core 和 System.Linq.Dynamic.Core 库有很好的理解。我利用这些库和 LINQ 来解决问题。

我追求的是通用解决方案和可重用代码。否则,对于每个表格,我都需要为每个新实体重新编写专门的过滤和排序方法。

这是代码

//HomeController.cs ======================================

//this is target of AJAX call and provides data for
//the table, based on selected input parameters
public IActionResult EmployeesDT(DataTables.AspNet.Core.IDataTablesRequest request)
{
    // There is dependency in this method on names of fields
    // and implied mapping. I see it almost impossible to avoid.
    // At least, in this method, we avoided dependency on the order
    // of table fields, in case order needs to be changed
    //Here are our mapped table columns:
    //Column0 id -> Employee.Id
    //Column1 givenName -> Employee.FirstName
    //Column2 familyName -> Employee.LastName
    //Column3 town -> Employee.City
    //Column4 country -> Employee.Country
    //Column5 email -> Employee.Email
    //Column6 phoneNo -> Employee.Phone

    try
    {
        IQueryable<Employee> employees = MockDatabase.MockDatabase.Instance.EmployeesTable.AsQueryable();

        //here we get the count that needs to be presented by the UI
        int totalRecordsCount = employees.Count();

        var iQueryableOfAnonymous = employees.Select(p => new
        {
            id = p.Id,
            givenName = p.FirstName,
            familyName = p.LastName,
            town = p.City,
            country = p.Country,
            email = p.Email,
            phoneNo = p.Phone,
        });

        iQueryableOfAnonymous = FilterRowsPerRequestParameters(iQueryableOfAnonymous, request);

        //here we get the count that needs to be presented by the UI
        int filteredRecordsCount = iQueryableOfAnonymous.Count();

        iQueryableOfAnonymous = SortRowsPerRequestParamters(iQueryableOfAnonymous, request);

        iQueryableOfAnonymous = iQueryableOfAnonymous.Skip(request.Start).Take(request.Length);

        //here we materialize the query
        var dataPage = iQueryableOfAnonymous.ToList();

        var response = DataTablesResponse.Create(request, totalRecordsCount, filteredRecordsCount, dataPage);

        return new DataTablesJsonResult(response, false);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
        var response = DataTablesResponse.Create(request, "Error processing AJAX call on server side");
        return new DataTablesJsonResult(response, false);
    }
}

private IQueryable<T> SortRowsPerRequestParamters<T>(
    IQueryable<T> iQueryableOfAnonymous, DataTables.AspNet.Core.IDataTablesRequest request)
{
    /*
    * So, in "IQueryable<T> iQueryableOfAnonymous" I have the source data that I need to 
    * sort. In "DataTables.AspNet.Core.IDataTablesRequest request" I have full specification 
    * of sorting required, including the specification of columns
    * that need to be sorted by marked with "IsSortable" and "Sort.Order". 
    * I use Reflection to check if names of entity T properties match column names in
    * DataTables.AspNet.Core.IDataTablesRequest request and then create sort Linq query using
    * System.Linq.Dynamic.Core library, and return Linq query as a result.
    * It might look complicated, but advantage is that this method is generic and can 
    * be applied many times on different entities T. 
    */

    if (request != null && request.Columns != null && request.Columns.Any())
    {
        //this will work if type T contains properties that have names column.Name
        var sortingColumns = request.Columns.Where(p => p.IsSortable && p.Sort != null).OrderBy(p => p.Sort.Order).ToList();

        Type objType = typeof(T);
        var ListOfAllPropertiesInT = objType.GetProperties().Select(p => p.Name).ToList();

        if (sortingColumns != null && sortingColumns.Count > 0)
        {
            //we plan to build dynamic Linq expression in this string
            string dynamicLinqOrder = string.Empty;
            bool isFirstString = true;

            for (int i = 0; i < sortingColumns.Count; i++)
            {
                var column = sortingColumns[i];

                //check if that property exists in T, 
                //otherwise we will create syntax error in dynamic linq
                if (ListOfAllPropertiesInT.Contains(column.Name))
                {
                    if (isFirstString)
                    {
                        isFirstString = false;
                    }
                    else
                    {
                        dynamicLinqOrder += ", ";
                    }

                    dynamicLinqOrder += column.Name;
                    if (column.Sort.Direction == SortDirection.Descending)
                    {
                        dynamicLinqOrder += " desc";
                    }
                }
            }

            if (dynamicLinqOrder.Length > 0)
            {
                //using System.Linq.Dynamic.Core
                iQueryableOfAnonymous = iQueryableOfAnonymous.OrderBy(dynamicLinqOrder);
            }
        };
    }

    return iQueryableOfAnonymous;
}

private IQueryable<T> FilterRowsPerRequestParameters<T>(
    IQueryable<T> iQueryableOfAnonymous, DataTables.AspNet.Core.IDataTablesRequest request)
{
    /*
     * So, in "IQueryable<T> iQueryableOfAnonymous" I have the source data that I need to 
     * filter. In "DataTables.AspNet.Core.IDataTablesRequest request" I have full specification 
     * of request, including the search value "request.Search.Value" and specification of columns
     * that need to be searched for marked by "IsSearchable". 
     * I use Reflection to check if names of entity T properties match column names in
     * DataTables.AspNet.Core.IDataTablesRequest request and then create filter Linq query using
     * System.Linq.Dynamic.Core library, and return Linq query as a result.
     * It might look complicated, but advantage is that this method is generic and can 
     * be applied many times on different entities T. 
     */
    //this will work if type T contains properties that have names column.Name
    if (request != null && request.Search != null && !System.String.IsNullOrEmpty(request.Search.Value))
    {
        string pattern = request.Search.Value?.Trim() ?? System.String.Empty;

        var searchingColumns = request.Columns.Where(p => p.IsSearchable).ToList();
        var config = new ParsingConfig { ResolveTypesBySimpleName = true };

        Type objType = typeof(T);
        var ListOfAllPropertiesInT = objType.GetProperties().Select(p => p.Name).ToList();

        if (searchingColumns.Count > 0)
        {
            //we plan to build dynamic Linq expression in this string
            string dynamicLinqSearch = string.Empty;
            bool isFirstString = true;

            for (int i = 0; i < searchingColumns.Count; i++)
            {
                var column = searchingColumns[i];

                //check if that property exists in T, 
                //otherwise we will create syntax error in dynamic linq
                if (ListOfAllPropertiesInT.Contains(column.Name))
                {
                    if (isFirstString)
                    {
                        isFirstString = false;
                    }
                    else
                    {
                        dynamicLinqSearch += " or ";
                    }

                    dynamicLinqSearch += $"""{column.Name}.Contains("{pattern}")""";
                }
            }

            if (dynamicLinqSearch.Length > 0)
            {
                //using System.Linq.Dynamic.Core
                iQueryableOfAnonymous = iQueryableOfAnonymous.Where(config, dynamicLinqSearch);
            }
        }
    }

    return iQueryableOfAnonymous;
}

11 结论

DataTables.net 组件看起来不错,并且在我的原型中运行得相当好。学习的成本不低,但如果组件在多个项目中频繁使用,它就能物有所值。如果您是 ASP.NET 开发人员,拥有这样一个组件作为工具集是很有益的。

开源代码的问题在于,您需要依赖社区来维护您集成到代码中的组件/库的最新版本。bug 总是会发生,这可能会成为一个问题。在此特定情况下,DataTables.AspNet.Core 库已不再维护,因此如果 DataTables.net 或 ASP.NET 的接口发生变化,您将需要自行修复问题。

可以下载完整的示例代码项目。

 

12 参考资料

[1] https://datatables.net.cn/

[2] https://nuget.net.cn/packages/DataTables.AspNet.Core/
DataTables.AspNet.Core

[3] https://github.com/ALMMa/datatables.aspnet

[4] https://www.c-sharpcorner.com/article/using-datatables-grid-with-asp-net-mvc/
在 ASP.NET MVC 中使用 DataTables 网格(MVC5)

[5] https://www.c-sharpcorner.com/article/display-loading-or-processing-message-inside-datatable/
在 DataTable 中显示加载或处理消息

[6] https://www.c-sharpcorner.com/article/create-datatable-in-jquery/
用 JQuery 创建一个 datatable(纯 jQuery)

[7] https://www.c-sharpcorner.com/article/server-side-rendering-of-datatables-js-in-asp-net-core/
在 ASP.NET Core 中服务器端渲染 DataTables JS (2024)

[8] https://www.c-sharpcorner.com/article/effortless-pagination-with-jquery-datatables-and-bootstrap/
使用 jQuery DataTables 和 Bootstrap 实现轻松分页(非常基础)

[9] https://www.c-sharpcorner.com/article/asp-net-mvc-jquery-server-side-datatable-example/
ASP.NET MVC jQuery 服务器端 Datatable 示例

[10] https://www.c-sharpcorner.com/article/pagination-in-mvc-with-jquery-datatable/
在 MVC 中使用 Jquery DataTable 实现分页

[11] https://codewithmukesh.com/blog/jquery-datatable-in-aspnet-core/#google_vignette
ASP.NET Core 中的 JQuery Datatable – 服务器端处理

[12] https://nuget.net.cn/packages/System.Linq.Dynamic.Core
System.Linq.Dynamic.Core

[13] https://github.com/zzzprojects/System.Linq.Dynamic.Core
zzzprojects/ System.Linq.Dynamic.Core

[14] https://dynamic-linq.net/
一个免费且开源的 LINQ 动态查询库

[21] ASP.NET8 使用 DataTables.net – 第1部分 – 基础
https://codeproject.org.cn/Articles/5385033/ASP-NET-8-Using-DataTables-net-Part1-Foundation

[22] ASP.NET8 使用 DataTables.net – 第2部分 – 操作按钮
https://codeproject.org.cn/Articles/5385098/ASP-NET8-using-DataTables-net-Part2-Action-buttons

[23] ASP.NET8 使用 DataTables.net – 第3部分 – 状态保存
https://codeproject.org.cn/Articles/5385308/ASP-NET8-using-DataTables-net-Part3-State-saving

[24] ASP.NET8 使用 DataTables.net – 第4部分 – 多语言
https://codeproject.org.cn/Articles/5385407/ASP-NET8-using-DataTables-net-Part4-Multilingual

[25] ASP.NET8 使用 DataTables.net – 第5部分 – 在 AJAX 中传递附加参数
https://codeproject.org.cn/Articles/5385575/ASP-NET8-using-DataTables-net-Part5-Passing-additi

[26] ASP.NET8 使用 DataTables.net – 第6部分 – 在 AJAX 中返回附加参数
https://codeproject.org.cn/Articles/5385692/ASP-NET8-using-DataTables-net-Part6-Returning-addi

[27] ASP.NET8 使用 DataTables.net – 第7部分 – 常规按钮
https://codeproject.org.cn/Articles/5385828/ASP-NET8-using-DataTables-net-Part7-Buttons-regula

[28] ASP.NET8 使用 DataTables.net – 第8部分 – 选择行
https://codeproject.org.cn/Articles/5386103/ASP-NET8-using-DataTables-net-Part8-Select-rows

[29] ASP.NET8 使用 DataTables.net – 第9部分 – 高级筛选器
https://codeproject.org.cn/Articles/5386263/ASP-NET8-using-DataTables-net-Part9-Advanced-Filte

 

 

© . All rights reserved.