AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。
Entity Framework提供的排序功能
再來回顧一下上篇文章,載入使用者列表並進行排序資料庫分頁的程式碼:
var q = DB.Users.Include(u => u.Dept); // 在使用者名稱稱中搜尋 string searchText = ttbSearchMessage.Text.Trim(); if (!String.IsNullOrEmpty(searchText)) { q = q.Where(u => u.Name.Contains(searchText) || u.ChineseName.Contains(searchText) || u.EnglishName.Contains(searchText)); } // 過濾啟用狀態 if (rblEnableStatus.SelectedValue != "all") { q = q.Where(u => u.Enabled == (rblEnableStatus.SelectedValue == "enabled" ? true : false)); } // 在查詢新增之後,排序和分頁之前獲取總記錄數 Grid1.RecordCount = q.Count(); // 排列 q = q.OrderBy(u => u.Name); // 資料庫分頁 q = q.Skip(Grid1.PageIndex * Grid1.PageSize).Take(Grid1.PageSize); Grid1.DataSource = q; Grid1.DataBind();
讓我們把關注點集中到排序程式碼上:
q = q.OrderBy(u => u.Name);
在FineUI實際應用中,我們一般是從表格的 SortField 中讀取排序欄位,顯然EF提供的OrderBy無法接受字串表示的排序欄位。
手工建立Lamba表示式
透過搜尋發現了這個帖子:http://stackoverflow.com/questions/10072250/generic-funct-k-to-sort-collections-of-different-types/10074873
據此我們可以寫出如下的程式碼:
public Expression<Func<T, To>> GetSortExpression<T, To>(String sortBy) { var param = Expression.Parameter(typeof(T), "x"); Expression expr = Expression.Property(param, sortBy); return Expression.Lambda<Func<T, To>>(expr, param); } protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { string sortField = grid.SortField; if (grid.SortDirection == "ASC") { q = q.OrderBy(GetSortExpression<T, object>(sortField)); } else { q = q.OrderByDescending(GetSortExpression<T, object>(sortField)); } return q; }
經過測試,我們發現這個方法不支援bool, int, DateTime, DateTime?型別的列排序。
經過擴充套件後的程式碼如下所示:
protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { string sortField = grid.SortField; var propertyType = typeof(T).GetProperty(sortField).PropertyType; if (grid.SortDirection == "ASC") { if (propertyType == typeof(bool)) { q = q.OrderBy(GetSortExpression<T, bool>(sortField)); } else if (propertyType == typeof(int)) { q = q.OrderBy(GetSortExpression<T, int>(sortField)); } else if (propertyType == typeof(DateTime)) { q = q.OrderBy(GetSortExpression<T, DateTime>(sortField)); } else if (propertyType == typeof(DateTime?)) { q = q.OrderBy(GetSortExpression<T, DateTime?>(sortField)); } else { q = q.OrderBy(GetSortExpression<T, object>(sortField)); } } else { if (propertyType == typeof(bool)) { q = q.OrderByDescending(GetSortExpression<T, bool>(sortField)); } else if (propertyType == typeof(int)) { q = q.OrderByDescending(GetSortExpression<T, int>(sortField)); } else if (propertyType == typeof(DateTime)) { q = q.OrderByDescending(GetSortExpression<T, DateTime>(sortField)); } else if (propertyType == typeof(DateTime?)) { q = q.OrderByDescending(GetSortExpression<T, DateTime?>(sortField)); } else { q = q.OrderByDescending(GetSortExpression<T, object>(sortField)); } } return q; }
但這種做法過於臃腫,有沒有更好的辦法呢?
更好的SortBy擴充套件方法
後來,我們發現了這篇文章:http://stackoverflow.com/questions/3945645/sorting-gridview-with-entity-framework
透過對 IQueryable<T> 進行擴充套件,提供了接受類似 "Name DESC", "CreateTime", "CreateTime DESC" 引數的 SortBy 方法,更具有通用性。
原始的SortBy擴充套件方法:
public static class QueryExtensions { public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string propertyName) { if (source == null) { throw new ArgumentNullException("source"); } // DataSource control passes the sort parameter with a direction // if the direction is descending int descIndex = propertyName.IndexOf(" DESC"); if (descIndex >= 0) { propertyName = propertyName.Substring(0, descIndex).Trim(); } if (String.IsNullOrEmpty(propertyName)) { return source; } ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty); MemberExpression property = Expression.Property(parameter, propertyName); LambdaExpression lambda = Expression.Lambda(property, parameter); string methodName = (descIndex < 0) ? "OrderBy" : "OrderByDescending"; Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(lambda)); return source.Provider.CreateQuery<T>(methodCallExpression); } }
不過這個方法不支援"Name ASC"形式的引數,所以我們進行了簡單的修正,修正後的SortBy擴充套件方法:
public static class QueryExtensions { public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string sortExpression) { if (source == null) { throw new ArgumentNullException("source"); } string sortDirection = String.Empty; string propertyName = String.Empty; sortExpression = sortExpression.Trim(); int spaceIndex = sortExpression.Trim().IndexOf(" "); if (spaceIndex < 0) { propertyName = sortExpression; sortDirection = "ASC"; } else { propertyName = sortExpression.Substring(0, spaceIndex); sortDirection = sortExpression.Substring(spaceIndex + 1).Trim(); } if (String.IsNullOrEmpty(propertyName)) { return source; } ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty); MemberExpression property = Expression.Property(parameter, propertyName); LambdaExpression lambda = Expression.Lambda(property, parameter); string methodName = (sortDirection == "ASC") ? "OrderBy" : "OrderByDescending"; Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(lambda)); return source.Provider.CreateQuery<T>(methodCallExpression); } }
最佳化後的排序分頁程式碼
首先在頁面基類PageBase中定義排序和分頁的程式碼(使用了前面定義的 SortBy 擴充套件函式):
protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { return q.SortBy(grid.SortField + " " + grid.SortDirection); } protected IQueryable<T> SortAndPage<T>(IQueryable<T> q, FineUI.Grid grid) { return Sort(q, grid).Skip(grid.PageIndex * grid.PageSize).Take(grid.PageSize); }
最終查詢使用者列表的程式碼:
var q = DB.Users.Include(u => u.Dept); // 在使用者名稱稱中搜尋 string searchText = ttbSearchMessage.Text.Trim(); if (!String.IsNullOrEmpty(searchText)) { q = q.Where(u => u.Name.Contains(searchText) || u.ChineseName.Contains(searchText) || u.EnglishName.Contains(searchText)); } // 過濾啟用狀態 if (rblEnableStatus.SelectedValue != "all") { q = q.Where(u => u.Enabled == (rblEnableStatus.SelectedValue == "enabled" ? true : false)); } // 在查詢新增之後,排序和分頁之前獲取總記錄數 Grid1.RecordCount = q.Count(); // 排列和資料庫分頁 q = SortAndPage<User>(q, Grid1); Grid1.DataSource = q; Grid1.DataBind();
下載或捐贈AppBox
1. AppBox v2.1 是免費軟體,免費提供下載:http://fineui.com/bbs/forum.php?mod=viewthread&tid=3788
2. AppBox v3.0 是捐贈軟體,你可以透過捐贈作者來獲取AppBox v3.0的全部原始碼(http://fineui.com/donate/)。