首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

構建 ASP.NET MVC4+EF5+EasyUI+Unity2.x 後台管理系統(17):LINQ 動態排序與分頁通用解決方案

     編輯:關於ASP.NET

在處理 EasyUI DataGrid 的伺服器端分頁與排序時,傳統寫法需要針對每個欄位撰寫 switch-case 判斷,不僅程式碼冗長難維護,還容易出現拼字錯誤(如將 sort 寫成 sord 導致排序失效)。本篇介紹如何運用 Expression Tree 運算式樹 與 反射機制,封裝通用的 LinqHelper 類別,將 20 行排序邏輯精簡為 2 行呼叫。

問題場景:傳統硬編碼排序的痛點

// 原始碼:每新增一個可排序欄位就要修改此處
if (pager.order == "desc") {
    switch (pager.sort) {
        case "CreateTime": queryData = queryData.OrderByDescending(c => c.CreateTime); break;
        case "Name": queryData = queryData.OrderByDescending(c => c.Name); break;
        default: queryData = queryData.OrderByDescending(c => c.CreateTime); break;
    }
} else {
    switch (pager.sort) {
        case "CreateTime": queryData = queryData.OrderBy(c => c.CreateTime); break;
        case "Name": queryData = queryData.OrderBy(c => c.Name); break;
        default: queryData = queryData.OrderBy(c => c.CreateTime); break;
    }
}

缺點:違反開放封閉原則、維護成本高、易出現拼字錯誤、無法動態擴充。

解決方案:LinqHelper 通用排序分頁類別

在 App.Common 專案下建立 LinqHelper.cs,提供三大核心方法:

完整程式碼實作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace App.Common {
    public static class LinqHelper {
        
        /// 
        /// 動態排序:依欄位名稱與方向對 IQueryable 進行 OrderBy/OrderByDescending
        /// 
        public static IQueryable<T> DataSorting<T>(IQueryable<T> source, string sortExpression, string sortDirection) {
            if (string.IsNullOrEmpty(sortExpression)) return source;
            
            string sortingDir = sortDirection.ToUpper().Trim() == "ASC" ? "OrderBy" : "OrderByDescending";
            
            // 建立參數運算式:x => x.PropertyName
            ParameterExpression param = Expression.Parameter(typeof(T), "x");
            PropertyInfo pi = typeof(T).GetProperty(sortExpression);
            
            if (pi == null) throw new ArgumentException($"型別 {typeof(T).Name} 不包含屬性 {sortExpression}");
            
            // 建立屬性存取運算式:x.PropertyName
            Expression propertyAccess = Expression.Property(param, pi);
            
            // 建立 Lambda:x => x.PropertyName
            LambdaExpression lambda = Expression.Lambda(propertyAccess, param);
            
            // 呼叫 Queryable.OrderBy/OrderByDescending 方法
            MethodCallExpression expr = Expression.Call(
                typeof(Queryable), 
                sortingDir, 
                new Type[] { typeof(T), pi.PropertyType }, 
                source.Expression, 
                lambda
            );
            
            return source.Provider.CreateQuery<T>(expr);
        }

        /// 
        /// 動態分頁:支援首頁與非首頁不同邏輯
        /// 
        public static IQueryable<T> DataPaging<T>(IQueryable<T> source, int pageNumber, int pageSize) {
            if (pageNumber <= 1) return source.Take(pageSize);
            return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
        }

        /// 
        /// 排序並分頁一鍵完成
        /// 
        public static IQueryable<T> SortingAndPaging<T>(IQueryable<T> source, string sortExpression, string sortDirection, int pageNumber, int pageSize) {
            var query = DataSorting(source, sortExpression, sortDirection);
            return DataPaging(query, pageNumber, pageSize);
        }
    }
}

核心原理解析

  1. Expression.Parameter:建立輸入參數 x
  2. PropertyInfo:透過反射取得目標屬性中繼資料
  3. Expression.Property:建立屬性存取運算式 x.PropertyName
  4. Expression.Lambda:包裝成 Lambda 運算式 x => x.PropertyName
  5. Expression.Call:動態呼叫 Queryable.OrderBy 或 OrderByDescending 靜態方法
  6. Provider.CreateQuery:將運算式樹轉換回 IQueryable<T>

實際呼叫範例

在 BLL 或 Repository 層中:

// 排序 + 分頁一行搞定
var pagedData = LinqHelper.SortingAndPaging<SysSampleModel>(
    source: queryData,
    sortExpression: pager.sort,      // 來自 EasyUI DataGrid 的 sort 參數
    sortDirection: pager.order,      // 來自 EasyUI DataGrid 的 order 參數
    pageNumber: pager.page,          // 當前頁碼
    pageSize: pager.rows             // 每頁筆數
);

進階擴充建議

  • 多欄位排序:支援 sort="CreateTime,Name" 格式,鏈式呼叫 ThenBy
  • 巢狀屬性支援:解析 "Department.Name" 等導航屬性路徑
  • 忽略大小寫比較:字串排序時自動套用 StringComparer.OrdinalIgnoreCase
  • 快取 PropertyInfo:使用 ConcurrentDictionary 快取反射結果提升效能

修正記錄

本篇同步修正了 GridPager 類別中的拼字錯誤:將屬性 sord 更正為 sort,解決後端無法接收 DataGrid 排序參數的 Bug。


感謝第 8 講留言區 @nyth 與 @紅顏醉丶帝 的建議啟發了此重構方向。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved